diff --git a/docs/src/test/java/org/elasticsearch/smoketest/XDocsClientYamlTestSuiteIT.java b/docs/src/test/java/org/elasticsearch/smoketest/XDocsClientYamlTestSuiteIT.java index 353afc6e9df..d44e6314467 100644 --- a/docs/src/test/java/org/elasticsearch/smoketest/XDocsClientYamlTestSuiteIT.java +++ b/docs/src/test/java/org/elasticsearch/smoketest/XDocsClientYamlTestSuiteIT.java @@ -29,6 +29,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; +import static org.hamcrest.Matchers.is; public class XDocsClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { private static final String USER_TOKEN = basicAuthHeaderValue("test_admin", new SecureString("x-pack-test-password".toCharArray())); @@ -78,7 +79,23 @@ public class XDocsClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { */ @After public void reenableWatcher() throws Exception { - getAdminExecutionContext().callApi("xpack.watcher.start", emptyMap(), emptyList(), emptyMap()); + if (isWatcherTest()) { + assertBusy(() -> { + ClientYamlTestResponse response = + getAdminExecutionContext().callApi("xpack.watcher.stats", emptyMap(), emptyList(), emptyMap()); + String state = (String) response.evaluate("stats.0.watcher_state"); + if (state.equals("started") == false || state.equals("starting") == false) { + getAdminExecutionContext().callApi("xpack.watcher.start", emptyMap(), emptyList(), emptyMap()); + } + + assertThat(state, is("started")); + }); + } + } + + private boolean isWatcherTest() { + String testName = getTestName(); + return testName != null && testName.contains("watcher"); } /** diff --git a/plugin/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java b/plugin/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java index 5f694bc38c1..77e813b17b9 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java @@ -154,7 +154,6 @@ public class HttpClient extends AbstractComponent { // timeouts if (request.connectionTimeout() != null) { - config.setConnectTimeout(Math.toIntExact(request.connectionTimeout.millis())); } else { config.setConnectTimeout(Math.toIntExact(defaultConnectionTimeout.millis())); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java b/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java index 9e2e2413886..362b3afdf5f 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ml/MachineLearning.java @@ -155,8 +155,9 @@ public class MachineLearning implements ActionPlugin { public static final Setting AUTODETECT_PROCESS = Setting.boolSetting("xpack.ml.autodetect_process", true, Property.NodeScope); public static final Setting ML_ENABLED = - Setting.boolSetting("node.ml", XPackSettings.MACHINE_LEARNING_ENABLED, Setting.Property.NodeScope); + Setting.boolSetting("node.ml", XPackSettings.MACHINE_LEARNING_ENABLED, Property.NodeScope); public static final String ML_ENABLED_NODE_ATTR = "ml.enabled"; + public static final String MAX_OPEN_JOBS_NODE_ATTR = "ml.max_open_jobs"; public static final Setting CONCURRENT_JOB_ALLOCATIONS = Setting.intSetting("xpack.ml.node_concurrent_job_allocations", 2, 0, Property.Dynamic, Property.NodeScope); @@ -200,7 +201,10 @@ public class MachineLearning implements ActionPlugin { Settings.Builder additionalSettings = Settings.builder(); Boolean allocationEnabled = ML_ENABLED.get(settings); if (allocationEnabled != null && allocationEnabled) { + // TODO: the simple true/false flag will not be required once all supported versions have the number - consider removing in 7.0 additionalSettings.put("node.attr." + ML_ENABLED_NODE_ATTR, "true"); + additionalSettings.put("node.attr." + MAX_OPEN_JOBS_NODE_ATTR, + AutodetectProcessManager.MAX_RUNNING_JOBS_PER_NODE.get(settings)); } return additionalSettings.build(); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ml/action/OpenJobAction.java b/plugin/src/main/java/org/elasticsearch/xpack/ml/action/OpenJobAction.java index 369f77319bd..6427de84d55 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ml/action/OpenJobAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ml/action/OpenJobAction.java @@ -42,7 +42,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ObjectParser; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; @@ -566,14 +565,20 @@ public class OpenJobAction extends Action unavailableIndices = verifyIndicesPrimaryShardsAreActive(jobId, clusterState); if (unavailableIndices.size() != 0) { String reason = "Not opening job [" + jobId + "], because not all primary shards are active for the following indices [" + @@ -716,6 +722,20 @@ public class OpenJobAction extends Action implements Writeable, ToXContentO public void setGroups(List groups) { this.groups = groups == null ? Collections.emptyList() : groups; - for (String group : this.groups) { - if (MlStrings.isValidId(group) == false) { - throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_GROUP, group)); - } - } } public Date getCreateTime() { @@ -994,6 +989,8 @@ public class Job extends AbstractDiffable implements Writeable, ToXContentO throw new IllegalArgumentException(Messages.getMessage(Messages.JOB_CONFIG_ID_TOO_LONG, MAX_JOB_ID_LENGTH)); } + validateGroups(); + // Results index name not specified in user input means use the default, so is acceptable in this validation if (!Strings.isNullOrEmpty(resultsIndexName) && !MlStrings.isValidId(resultsIndexName)) { throw new IllegalArgumentException( @@ -1003,6 +1000,14 @@ public class Job extends AbstractDiffable implements Writeable, ToXContentO // Creation time is NOT required in user input, hence validated only on build } + private void validateGroups() { + for (String group : this.groups) { + if (MlStrings.isValidId(group) == false) { + throw new IllegalArgumentException(Messages.getMessage(Messages.INVALID_GROUP, group)); + } + } + } + /** * Builds a job with the given {@code createTime} and the current version. * This should be used when a new job is created as opposed to {@link #build()}. diff --git a/plugin/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessor.java b/plugin/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessor.java index 1aba5303285..2c9c7bf564b 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessor.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessor.java @@ -73,8 +73,11 @@ public class StateProcessor extends AbstractComponent { // No more zero bytes in this block break; } - // No validation - assume the native process has formatted the state correctly - persist(jobId, bytesRef.slice(splitFrom, nextZeroByte - splitFrom)); + // Ignore completely empty chunks + if (nextZeroByte > splitFrom) { + // No validation - assume the native process has formatted the state correctly + persist(jobId, bytesRef.slice(splitFrom, nextZeroByte - splitFrom)); + } splitFrom = nextZeroByte + 1; } if (splitFrom >= bytesRef.length()) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java index 56ebc471467..4ba172ab524 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/Monitoring.java @@ -31,7 +31,6 @@ import org.elasticsearch.xpack.monitoring.collector.Collector; import org.elasticsearch.xpack.monitoring.collector.cluster.ClusterStatsCollector; import org.elasticsearch.xpack.monitoring.collector.indices.IndexRecoveryCollector; import org.elasticsearch.xpack.monitoring.collector.indices.IndexStatsCollector; -import org.elasticsearch.xpack.monitoring.collector.indices.IndicesStatsCollector; import org.elasticsearch.xpack.monitoring.collector.ml.JobStatsCollector; import org.elasticsearch.xpack.monitoring.collector.node.NodeStatsCollector; import org.elasticsearch.xpack.monitoring.collector.shards.ShardsCollector; @@ -117,7 +116,6 @@ public class Monitoring implements ActionPlugin { final Exporters exporters = new Exporters(settings, exporterFactories, clusterService, licenseState, threadPool.getThreadContext()); Set collectors = new HashSet<>(); - collectors.add(new IndicesStatsCollector(settings, clusterService, monitoringSettings, licenseState, client)); collectors.add(new IndexStatsCollector(settings, clusterService, monitoringSettings, licenseState, client)); collectors.add(new ClusterStatsCollector(settings, clusterService, monitoringSettings, licenseState, client, licenseService)); collectors.add(new ShardsCollector(settings, clusterService, monitoringSettings, licenseState)); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java index 2caa4c8184e..3bb4f2557fb 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/MonitoringSettings.java @@ -57,12 +57,6 @@ public class MonitoringSettings extends AbstractComponent { public static final Setting INDEX_STATS_TIMEOUT = timeSetting(collectionKey("index.stats.timeout"), TimeValue.timeValueSeconds(10), Property.Dynamic, Property.NodeScope); - /** - * Timeout value when collecting total indices statistics (default to 10s) - */ - public static final Setting INDICES_STATS_TIMEOUT = - 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) */ @@ -127,7 +121,6 @@ public class MonitoringSettings extends AbstractComponent { INTERVAL, INDEX_RECOVERY_TIMEOUT, INDEX_STATS_TIMEOUT, - INDICES_STATS_TIMEOUT, INDEX_RECOVERY_ACTIVE_ONLY, CLUSTER_STATE_TIMEOUT, CLUSTER_STATS_TIMEOUT, @@ -142,7 +135,6 @@ public class MonitoringSettings extends AbstractComponent { private volatile TimeValue indexStatsTimeout; - private volatile TimeValue indicesStatsTimeout; private volatile TimeValue clusterStateTimeout; private volatile TimeValue clusterStatsTimeout; private volatile TimeValue recoveryTimeout; @@ -155,8 +147,6 @@ public class MonitoringSettings extends AbstractComponent { setIndexStatsTimeout(INDEX_STATS_TIMEOUT.get(settings)); clusterSettings.addSettingsUpdateConsumer(INDEX_STATS_TIMEOUT, this::setIndexStatsTimeout); - setIndicesStatsTimeout(INDICES_STATS_TIMEOUT.get(settings)); - clusterSettings.addSettingsUpdateConsumer(INDICES_STATS_TIMEOUT, this::setIndicesStatsTimeout); setIndices(INDICES.get(settings)); clusterSettings.addSettingsUpdateConsumer(INDICES, this::setIndices); setClusterStateTimeout(CLUSTER_STATE_TIMEOUT.get(settings)); @@ -175,8 +165,6 @@ public class MonitoringSettings extends AbstractComponent { return indexStatsTimeout; } - public TimeValue indicesStatsTimeout() { return indicesStatsTimeout; } - public String[] indices() { return indices; } @@ -205,10 +193,6 @@ public class MonitoringSettings extends AbstractComponent { this.indexStatsTimeout = indexStatsTimeout; } - private void setIndicesStatsTimeout(TimeValue indicesStatsTimeout) { - this.indicesStatsTimeout = indicesStatsTimeout; - } - private void setClusterStateTimeout(TimeValue clusterStateTimeout) { this.clusterStateTimeout = clusterStateTimeout; } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollector.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollector.java index 103e998bffa..8709ab1b80b 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollector.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollector.java @@ -24,10 +24,10 @@ import java.util.Collections; import java.util.List; /** - * Collector for indices statistics. + * Collector for indices and singular index statistics. *

- * This collector runs on the master node only and collect a {@link IndexStatsMonitoringDoc} - * document for each existing index in the cluster. + * This collector runs on the master node only and collect a single {@link IndicesStatsMonitoringDoc} for the cluster and a + * {@link IndexStatsMonitoringDoc} document for each existing index in the cluster. */ public class IndexStatsCollector extends Collector { @@ -47,8 +47,8 @@ public class IndexStatsCollector extends Collector { @Override protected Collection doCollect() throws Exception { - List results = new ArrayList<>(); - IndicesStatsResponse indicesStats = client.admin().indices().prepareStats() + final List results = new ArrayList<>(); + final IndicesStatsResponse indicesStats = client.admin().indices().prepareStats() .setIndices(monitoringSettings.indices()) .setIndicesOptions(IndicesOptions.lenientExpandOpen()) .clear() @@ -64,14 +64,18 @@ public class IndexStatsCollector extends Collector { .setRequestCache(true) .get(monitoringSettings.indexStatsTimeout()); - long timestamp = System.currentTimeMillis(); - String clusterUUID = clusterUUID(); - DiscoveryNode sourceNode = localNode(); + final long timestamp = System.currentTimeMillis(); + final String clusterUUID = clusterUUID(); + final DiscoveryNode sourceNode = localNode(); + // add the indices stats that we use to collect the index stats + results.add(new IndicesStatsMonitoringDoc(monitoringId(), monitoringVersion(), clusterUUID, timestamp, sourceNode, indicesStats)); + + // collect each index stats document for (IndexStats indexStats : indicesStats.getIndices().values()) { - results.add(new IndexStatsMonitoringDoc(monitoringId(), monitoringVersion(), - clusterUUID, timestamp, sourceNode, indexStats)); + results.add(new IndexStatsMonitoringDoc(monitoringId(), monitoringVersion(), clusterUUID, timestamp, sourceNode, indexStats)); } + return Collections.unmodifiableCollection(results); } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsCollector.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsCollector.java deleted file mode 100644 index 70fee53a1ed..00000000000 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsCollector.java +++ /dev/null @@ -1,61 +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.monitoring.collector.indices; - -import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; -import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.client.Client; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.XPackLicenseState; -import org.elasticsearch.xpack.monitoring.MonitoringSettings; -import org.elasticsearch.xpack.monitoring.collector.Collector; -import org.elasticsearch.xpack.monitoring.exporter.MonitoringDoc; -import org.elasticsearch.xpack.security.InternalClient; - -import java.util.Collection; -import java.util.Collections; - -/** - * Collector for indices statistics. - *

- * This collector runs on the master node only and collect one {@link IndicesStatsMonitoringDoc} - * document. - */ -public class IndicesStatsCollector extends Collector { - - private final Client client; - - public IndicesStatsCollector(Settings settings, ClusterService clusterService, - MonitoringSettings monitoringSettings, - XPackLicenseState licenseState, InternalClient client) { - super(settings, "indices-stats", clusterService, monitoringSettings, licenseState); - this.client = client; - } - - @Override - protected boolean shouldCollect() { - return super.shouldCollect() && isLocalNodeMaster(); - } - - @Override - protected Collection doCollect() throws Exception { - IndicesStatsResponse indicesStats = client.admin().indices().prepareStats() - .setIndices(monitoringSettings.indices()) - .setIndicesOptions(IndicesOptions.lenientExpandOpen()) - .clear() - .setDocs(true) - .setIndexing(true) - .setSearch(true) - .setStore(true) - .get(monitoringSettings.indicesStatsTimeout()); - - IndicesStatsMonitoringDoc indicesStatsDoc = new IndicesStatsMonitoringDoc(monitoringId(), - monitoringVersion(), clusterUUID(), System.currentTimeMillis(), localNode(), - indicesStats); - return Collections.singletonList(indicesStatsDoc); - } -} diff --git a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDoc.java b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDoc.java index f170661f40f..457ec4d5637 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDoc.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsMonitoringDoc.java @@ -10,7 +10,7 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.xpack.monitoring.exporter.MonitoringDoc; /** - * Monitoring document collected by {@link IndicesStatsCollector} + * Monitoring document collected by {@link IndexStatsCollector} */ public class IndicesStatsMonitoringDoc extends MonitoringDoc { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/IntegrationAccount.java b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/IntegrationAccount.java index 60f74d037d0..b6d8b1851eb 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/IntegrationAccount.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/IntegrationAccount.java @@ -90,8 +90,7 @@ public class IntegrationAccount extends HipChatAccount { response)); } catch (Exception e) { logger.error("failed to execute hipchat api http request", e); - sentMessages.add(SentMessages.SentMessage.error(room, SentMessages.SentMessage.TargetType.ROOM, message, - ExceptionsHelper.detailedMessage(e))); + sentMessages.add(SentMessages.SentMessage.error(room, SentMessages.SentMessage.TargetType.ROOM, message, e)); } return new SentMessages(name, sentMessages); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/SentMessages.java b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/SentMessages.java index be56fe4b4a9..e2fd3900e6a 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/SentMessages.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/SentMessages.java @@ -5,7 +5,10 @@ */ package org.elasticsearch.xpack.notification.hipchat; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -20,6 +23,9 @@ import java.util.Locale; public class SentMessages implements ToXContentObject, Iterable { + private static final ParseField ACCOUNT = new ParseField("account"); + private static final ParseField SENT_MESSAGES = new ParseField("sent_messages"); + private String accountName; private List messages; @@ -48,8 +54,8 @@ public class SentMessages implements ToXContentObject, Iterable= 200 && response.status() < 300; } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - if (failureReason != null) { - builder.field(Field.STATUS, "failure"); - builder.field(Field.REASON, failureReason); + builder.field(STATUS.getPreferredName(), isSuccess() ? "success" : "failure"); + if (isSuccess() == false) { + builder.field(STATUS.getPreferredName(), "failure"); if (request != null) { - builder.field(Field.REQUEST); + builder.field(REQUEST.getPreferredName()); request.toXContent(builder, params); } if (response != null) { - builder.field(Field.RESPONSE); + builder.field(RESPONSE.getPreferredName()); response.toXContent(builder, params); } - } else { - builder.field(Field.STATUS, "success"); + if (exception != null) { + ElasticsearchException.generateFailureXContent(builder, params, exception, true); + } } builder.field(targetType.fieldName, targetName); - builder.field(Field.MESSAGE); + builder.field(MESSAGE.getPreferredName()); message.toXContent(builder, params, false); return builder.endObject(); } - - private static String resolveFailureReason(HttpResponse response) { - int status = response.status(); - if (status < 300) { - return null; - } - switch (status) { - case 400: return "Bad Request"; - case 401: return "Unauthorized. The provided authentication token is invalid."; - case 403: return "Forbidden. The account doesn't have permission to send this message."; - case 404: // Not Found - case 405: // Method Not Allowed - case 406: return "The account used invalid HipChat APIs"; // Not Acceptable - case 503: - case 500: return "HipChat Server Error."; - default: - return "Unknown Error"; - } - } - } - - interface Field { - String ACCOUNT = new String("account"); - String SENT_MESSAGES = new String("sent_messages"); - String STATUS = new String("status"); - String REASON = new String("reason"); - String REQUEST = new String("request"); - String RESPONSE = new String("response"); - String MESSAGE = new String("message"); } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/UserAccount.java b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/UserAccount.java index 5ac87b13b5c..5eb5b34d6ec 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/UserAccount.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/UserAccount.java @@ -88,8 +88,7 @@ public class UserAccount extends HipChatAccount { response)); } catch (IOException e) { logger.error("failed to execute hipchat api http request", e); - sentMessages.add(SentMessages.SentMessage.error(room, SentMessages.SentMessage.TargetType.ROOM, message, - ExceptionsHelper.detailedMessage(e))); + sentMessages.add(SentMessages.SentMessage.error(room, SentMessages.SentMessage.TargetType.ROOM, message, e)); } } } @@ -102,8 +101,7 @@ public class UserAccount extends HipChatAccount { response)); } catch (Exception e) { logger.error("failed to execute hipchat api http request", e); - sentMessages.add(SentMessages.SentMessage.error(user, SentMessages.SentMessage.TargetType.USER, message, - ExceptionsHelper.detailedMessage(e))); + sentMessages.add(SentMessages.SentMessage.error(user, SentMessages.SentMessage.TargetType.USER, message, e)); } } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/V1Account.java b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/V1Account.java index 58079a8a444..c1970780393 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/V1Account.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/notification/hipchat/V1Account.java @@ -84,8 +84,7 @@ public class V1Account extends HipChatAccount { response)); } catch (Exception e) { logger.error("failed to execute hipchat api http request", e); - sentMessages.add(SentMessages.SentMessage.error(room, SentMessages.SentMessage.TargetType.ROOM, message, - ExceptionsHelper.detailedMessage(e))); + sentMessages.add(SentMessages.SentMessage.error(room, SentMessages.SentMessage.TargetType.ROOM, message, e)); } } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SentMessages.java b/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SentMessages.java index 540f3e376c0..475335bb649 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SentMessages.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SentMessages.java @@ -5,7 +5,10 @@ */ package org.elasticsearch.xpack.notification.slack; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -20,6 +23,9 @@ import java.util.List; public class SentMessages implements ToXContentObject, Iterable { + private static final ParseField ACCOUNT = new ParseField("account"); + private static final ParseField SENT_MESSAGES = new ParseField("sent_messages"); + private String accountName; private List messages; @@ -48,8 +54,8 @@ public class SentMessages implements ToXContentObject, Iterable= 200 && response.status() < 300; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - if (failureReason != null) { - builder.field(Field.STATUS, "failure"); - builder.field(Field.REASON, failureReason); + builder.field(STATUS.getPreferredName(), isSuccess() ? "success" : "failure"); + if (isSuccess() == false) { if (request != null) { - builder.field(Field.REQUEST); + builder.field(REQUEST.getPreferredName()); request.toXContent(builder, params); } if (response != null) { - builder.field(Field.RESPONSE); + builder.field(RESPONSE.getPreferredName()); response.toXContent(builder, params); } - } else { - builder.field(Field.STATUS, "success"); + if (exception != null) { + ElasticsearchException.generateFailureXContent(builder, params, exception, true); + } } if (to != null) { - builder.field(Field.TO, to); + builder.field(TO.getPreferredName(), to); } - builder.field(Field.MESSAGE); + builder.field(MESSAGE.getPreferredName()); message.toXContent(builder, params, false); return builder.endObject(); } - - private static String resolveFailureReason(HttpResponse response) { - int status = response.status(); - if (status < 300) { - return null; - } - if (status > 399 && status < 500) { - return "Bad Request"; - } - if (status > 499) { - return "Slack Server Error"; - } - return "Unknown Error"; - } - } - - interface Field { - String ACCOUNT = new String("account"); - String SENT_MESSAGES = new String("sent_messages"); - String STATUS = new String("status"); - String REASON = new String("reason"); - String REQUEST = new String("request"); - String RESPONSE = new String("response"); - String MESSAGE = new String("message"); - String TO = new String("to"); } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SlackAccount.java b/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SlackAccount.java index a57aac448b7..99d9bbb1c02 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SlackAccount.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/notification/slack/SlackAccount.java @@ -111,7 +111,7 @@ public class SlackAccount { return SentMessages.SentMessage.responded(to, message, request, response); } catch (Exception e) { logger.error("failed to execute slack api http request", e); - return SentMessages.SentMessage.error(to, message, ExceptionsHelper.detailedMessage(e)); + return SentMessages.SentMessage.error(to, message, e); } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/upgrade/Upgrade.java b/plugin/src/main/java/org/elasticsearch/xpack/upgrade/Upgrade.java index 28ef3153cd7..4d7cb9b0743 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/upgrade/Upgrade.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/upgrade/Upgrade.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.indices.IndexTemplateMissingException; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; @@ -290,13 +291,22 @@ public class Upgrade implements ActionPlugin { private static ActionListener deleteIndexTemplateListener(String name, ActionListener listener, Runnable runnable) { - return ActionListener.wrap(r -> { - if (r.isAcknowledged()) { - runnable.run(); - } else { - listener.onFailure(new ElasticsearchException("Deleting [{}] template was not acknowledged", name)); - } - }, listener::onFailure); + return ActionListener.wrap( + r -> { + if (r.isAcknowledged()) { + runnable.run(); + } else { + listener.onFailure(new ElasticsearchException("Deleting [{}] template was not acknowledged", name)); + } + }, + // if the index template we tried to delete is gone already, no need to worry + e -> { + if (e instanceof IndexTemplateMissingException) { + runnable.run(); + } else { + listener.onFailure(e); + } + }); } private static void startWatcherIfNeeded(Boolean shouldStartWatcher, Client client, ActionListener listener) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/Action.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/Action.java index c99e9de0e95..50dae042514 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/Action.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/Action.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.watcher.actions; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.common.xcontent.ToXContent; @@ -58,6 +59,8 @@ public interface Action extends ToXContentObject { */ public static class StoppedResult extends Result { + private static ParseField REASON = new ParseField("reason"); + private final String reason; protected StoppedResult(String type, Status status, String reason, Object... args) { @@ -71,7 +74,7 @@ public interface Action extends ToXContentObject { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder.field(Field.REASON.getPreferredName(), reason); + return builder.field(REASON.getPreferredName(), reason); } } @@ -85,7 +88,26 @@ public interface Action extends ToXContentObject { public Failure(String type, String reason, Object... args) { super(type, Status.FAILURE, reason, args); } + } + public static class FailureWithException extends Result { + + private final Exception exception; + + public FailureWithException(String type, Exception exception) { + super(type, Status.FAILURE); + this.exception = exception; + } + + public Exception getException() { + return exception; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + ElasticsearchException.generateFailureXContent(builder, params, exception, true); + return builder; + } } /** @@ -127,8 +149,4 @@ public interface Action extends ToXContentObject { A build(); } - - interface Field { - ParseField REASON = new ParseField("reason"); - } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java index 1664bd0ef89..8498c7c1507 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/ActionWrapper.java @@ -8,7 +8,6 @@ package org.elasticsearch.xpack.watcher.actions; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.ElasticsearchParseException; -import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.unit.TimeValue; @@ -138,9 +137,7 @@ public class ActionWrapper implements ToXContentObject { action.logger().error( (Supplier) () -> new ParameterizedMessage( "failed to execute action [{}/{}]. failed to transform payload.", ctx.watch().id(), id), e); - return new ActionWrapper.Result(id, conditionResult, null, - new Action.Result.Failure(action.type(), "Failed to transform payload. error: {}", - ExceptionsHelper.detailedMessage(e))); + return new ActionWrapper.Result(id, conditionResult, null, new Action.Result.FailureWithException(action.type(), e)); } } try { @@ -149,7 +146,7 @@ public class ActionWrapper implements ToXContentObject { } catch (Exception e) { action.logger().error( (Supplier) () -> new ParameterizedMessage("failed to execute action [{}/{}]", ctx.watch().id(), id), e); - return new ActionWrapper.Result(id, new Action.Result.Failure(action.type(), ExceptionsHelper.detailedMessage(e))); + return new ActionWrapper.Result(id, new Action.Result.FailureWithException(action.type(), e)); } } @@ -160,19 +157,15 @@ public class ActionWrapper implements ToXContentObject { ActionWrapper that = (ActionWrapper) o; - if (!id.equals(that.id)) return false; - if (condition != null ? !condition.equals(that.condition) : that.condition != null) return false; - if (transform != null ? !transform.equals(that.transform) : that.transform != null) return false; - return action.equals(that.action); + return Objects.equals(id, that.id) && + Objects.equals(condition, that.condition) && + Objects.equals(transform, that.transform) && + Objects.equals(action, that.action); } @Override public int hashCode() { - int result = id.hashCode(); - result = 31 * result + (condition != null ? condition.hashCode() : 0); - result = 31 * result + (transform != null ? transform.hashCode() : 0); - result = 31 * result + action.hashCode(); - return result; + return Objects.hash(id, condition, transform, action); } @Override @@ -189,7 +182,7 @@ public class ActionWrapper implements ToXContentObject { .endObject(); } if (transform != null) { - builder.startObject(Transform.Field.TRANSFORM.getPreferredName()) + builder.startObject(Transform.TRANSFORM.getPreferredName()) .field(transform.type(), transform, params) .endObject(); } @@ -215,7 +208,7 @@ public class ActionWrapper implements ToXContentObject { } else { if (Watch.Field.CONDITION.match(currentFieldName)) { condition = actionRegistry.getConditionRegistry().parseExecutable(watchId, parser); - } else if (Transform.Field.TRANSFORM.match(currentFieldName)) { + } else if (Transform.TRANSFORM.match(currentFieldName)) { transform = actionRegistry.getTransformRegistry().parse(watchId, parser); } else if (Throttler.Field.THROTTLE_PERIOD.match(currentFieldName)) { throttlePeriod = timeValueMillis(parser.longValue()); @@ -309,7 +302,7 @@ public class ActionWrapper implements ToXContentObject { builder.field(Watch.Field.CONDITION.getPreferredName(), condition, params); } if (transform != null) { - builder.field(Transform.Field.TRANSFORM.getPreferredName(), transform, params); + builder.field(Transform.TRANSFORM.getPreferredName(), transform, params); } action.toXContent(builder, params); return builder.endObject(); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/EmailAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/EmailAction.java index d21bf95373c..992a2ae3347 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/EmailAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/EmailAction.java @@ -282,7 +282,7 @@ public class EmailAction implements Action { } } - interface Field extends Action.Field { + interface Field { // common fields ParseField ACCOUNT = new ParseField("account"); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/ExecutableEmailAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/ExecutableEmailAction.java index 06dcceff5d1..45e83a62564 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/ExecutableEmailAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/email/ExecutableEmailAction.java @@ -57,7 +57,7 @@ public class ExecutableEmailAction extends ExecutableAction { Attachment attachment = parser.toAttachment(ctx, payload, emailAttachment); attachments.put(attachment.id(), attachment); } catch (ElasticsearchException | IOException e) { - return new EmailAction.Result.Failure(action.type(), e.getMessage()); + return new EmailAction.Result.FailureWithException(action.type(), e); } } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatAction.java index 6e3ae34e271..92e98d080f3 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatAction.java @@ -137,7 +137,7 @@ public class HipChatAction implements Action { boolean hasSuccesses = false; boolean hasFailures = false; for (SentMessages.SentMessage message : sentMessages) { - if (message.successful()) { + if (message.isSuccess()) { hasSuccesses = true; } else { hasFailures = true; diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/index/IndexAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/index/IndexAction.java index b369941ad9e..1ffa9ccc5df 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/index/IndexAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/index/IndexAction.java @@ -287,7 +287,7 @@ public class IndexAction implements Action { } } - interface Field extends Action.Field { + interface Field { ParseField INDEX = new ParseField("index"); ParseField DOC_TYPE = new ParseField("doc_type"); ParseField DOC_ID = new ParseField("doc_id"); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/logging/LoggingAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/logging/LoggingAction.java index 5851365c44f..31601197858 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/logging/LoggingAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/logging/LoggingAction.java @@ -186,7 +186,7 @@ public class LoggingAction implements Action { } } - interface Field extends Action.Field { + interface Field { ParseField CATEGORY = new ParseField("category"); ParseField LEVEL = new ParseField("level"); ParseField TEXT = new ParseField("text"); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/slack/SlackAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/slack/SlackAction.java index 1f2cf39feb0..a10ad30c0dd 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/slack/SlackAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/slack/SlackAction.java @@ -136,7 +136,7 @@ public class SlackAction implements Action { boolean hasSuccesses = false; boolean hasFailures = false; for (SentMessages.SentMessage message : sentMessages) { - if (message.successful()) { + if (message.isSuccess()) { hasSuccesses = true; } else { hasFailures = true; diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/ExecutableWebhookAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/ExecutableWebhookAction.java index 97487931601..3c34234f90e 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/ExecutableWebhookAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/ExecutableWebhookAction.java @@ -41,11 +41,10 @@ public class ExecutableWebhookAction extends ExecutableAction { HttpResponse response = httpClient.execute(request); - int status = response.status(); - if (status >= 400) { - logger.warn("received http status [{}] when connecting to watch action [{}/{}/{}]", status, ctx.watch().id(), type(), actionId); + if (response.status() >= 400) { return new WebhookAction.Result.Failure(request, response); + } else { + return new WebhookAction.Result.Success(request, response); } - return new WebhookAction.Result.Success(request, response); } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookAction.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookAction.java index 37066a4f61f..b67a59b7d38 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookAction.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookAction.java @@ -9,10 +9,10 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.xpack.watcher.actions.Action; import org.elasticsearch.xpack.common.http.HttpRequest; import org.elasticsearch.xpack.common.http.HttpRequestTemplate; import org.elasticsearch.xpack.common.http.HttpResponse; +import org.elasticsearch.xpack.watcher.actions.Action; import java.io.IOException; @@ -170,7 +170,7 @@ public class WebhookAction implements Action { } } - interface Field extends Action.Field { + interface Field { ParseField REQUEST = new ParseField("request"); ParseField RESPONSE = new ParseField("response"); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/client/WatchSourceBuilder.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/client/WatchSourceBuilder.java index 22ecf3effcb..7528c55d824 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/client/WatchSourceBuilder.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/client/WatchSourceBuilder.java @@ -195,7 +195,7 @@ public class WatchSourceBuilder extends ToXContentToBytes implements ToXContent .endObject(); } if (transform != null) { - builder.startObject(Transform.Field.TRANSFORM.getPreferredName()) + builder.startObject(Transform.TRANSFORM.getPreferredName()) .field(transform.type(), transform, params) .endObject(); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java index 193e474894f..6c0eafa5493 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java @@ -200,8 +200,7 @@ public class ExecutionService extends AbstractComponent { e -> { Throwable cause = ExceptionsHelper.unwrapCause(e); if (cause instanceof EsRejectedExecutionException) { - logger.debug("failed to store watch records due to overloaded threadpool [{}]", - ExceptionsHelper.detailedMessage(e)); + logger.debug("failed to store watch records due to filled up watcher threadpool"); } else { logger.warn("failed to store watch records", e); } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/WatchExecutionResult.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/WatchExecutionResult.java index 666b59d5cef..0addb8e61e3 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/WatchExecutionResult.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/execution/WatchExecutionResult.java @@ -82,7 +82,7 @@ public class WatchExecutionResult implements ToXContentObject { builder.field(Field.CONDITION.getPreferredName(), conditionResult, params); } if (transformResult != null) { - builder.field(Transform.Field.TRANSFORM.getPreferredName(), transformResult, params); + builder.field(Transform.TRANSFORM.getPreferredName(), transformResult, params); } builder.startArray(Field.ACTIONS.getPreferredName()); for (ActionWrapper.Result result : actionsResults.values()) { diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/Input.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/Input.java index 9ec5ecbe746..0f126f31954 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/Input.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/Input.java @@ -5,7 +5,8 @@ */ package org.elasticsearch.xpack.watcher.input; -import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -20,27 +21,31 @@ public interface Input extends ToXContentObject { abstract class Result implements ToXContentObject { + private static final ParseField STATUS = new ParseField("status"); + private static final ParseField TYPE = new ParseField("type"); + private static final ParseField PAYLOAD = new ParseField("payload"); + public enum Status { SUCCESS, FAILURE } protected final String type; protected final Status status; - private final String reason; private final Payload payload; + @Nullable private final Exception exception; protected Result(String type, Payload payload) { this.status = Status.SUCCESS; this.type = type; this.payload = payload; - this.reason = null; + this.exception = null; } protected Result(String type, Exception e) { this.status = Status.FAILURE; this.type = type; - this.reason = ExceptionsHelper.detailedMessage(e); this.payload = Payload.EMPTY; + this.exception = e; } public String type() { @@ -55,24 +60,24 @@ public interface Input extends ToXContentObject { return payload; } - public String reason() { + public Exception getException() { assert status == Status.FAILURE; - return reason; + return exception; } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.field(Field.TYPE.getPreferredName(), type); - builder.field(Field.STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT)); + builder.field(TYPE.getPreferredName(), type); + builder.field(STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT)); switch (status) { case SUCCESS: assert payload != null; - builder.field(Field.PAYLOAD.getPreferredName(), payload, params); + builder.field(PAYLOAD.getPreferredName(), payload, params); break; case FAILURE: - assert reason != null; - builder.field(Field.REASON.getPreferredName(), reason); + assert exception != null; + ElasticsearchException.generateFailureXContent(builder, params, exception, true); break; default: assert false; @@ -87,11 +92,4 @@ public interface Input extends ToXContentObject { interface Builder { I build(); } - - interface Field { - ParseField STATUS = new ParseField("status"); - ParseField TYPE = new ParseField("type"); - ParseField PAYLOAD = new ParseField("payload"); - ParseField REASON = new ParseField("reason"); - } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/http/HttpInput.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/http/HttpInput.java index 33ef650bb85..24199d192d8 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/http/HttpInput.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/http/HttpInput.java @@ -202,7 +202,7 @@ public class HttpInput implements Input { } } - interface Field extends Input.Field { + interface Field { ParseField REQUEST = new ParseField("request"); ParseField EXTRACT = new ParseField("extract"); ParseField STATUS_CODE = new ParseField("status_code"); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/search/SearchInput.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/search/SearchInput.java index 8f00846b91b..3cb4fe7a10e 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/search/SearchInput.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/input/search/SearchInput.java @@ -233,7 +233,7 @@ public class SearchInput implements Input { } } - public interface Field extends Input.Field { + public interface Field { ParseField REQUEST = new ParseField("request"); ParseField EXTRACT = new ParseField("extract"); ParseField TIMEOUT = new ParseField("timeout_in_millis"); diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/support/WatcherIndexTemplateRegistry.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/support/WatcherIndexTemplateRegistry.java index 2e77a4f2b60..80766bb548d 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/support/WatcherIndexTemplateRegistry.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/support/WatcherIndexTemplateRegistry.java @@ -36,8 +36,9 @@ public class WatcherIndexTemplateRegistry extends AbstractComponent implements C // version 2: added mappings for jira action // version 3: include watch status in history // version 6: upgrade to ES 6, removal of _status field + // version 7: add full exception stack traces for better debugging // Note: if you change this, also inform the kibana team around the watcher-ui - public static final String INDEX_TEMPLATE_VERSION = "6"; + public static final String INDEX_TEMPLATE_VERSION = "7"; public static final String HISTORY_TEMPLATE_NAME = ".watch-history-" + INDEX_TEMPLATE_VERSION; public static final String TRIGGERED_TEMPLATE_NAME = ".triggered_watches"; diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/Transform.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/Transform.java index 853cd52e278..6f4378231af 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/Transform.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/Transform.java @@ -5,7 +5,7 @@ */ package org.elasticsearch.xpack.watcher.transform; -import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.xcontent.ToXContent; @@ -18,10 +18,17 @@ import java.util.Locale; public interface Transform extends ToXContent { + ParseField TRANSFORM = new ParseField("transform"); + String type(); abstract class Result implements ToXContentObject { + private static final ParseField TYPE = new ParseField("type"); + private static final ParseField STATUS = new ParseField("status"); + private static final ParseField PAYLOAD = new ParseField("payload"); + private static final ParseField REASON = new ParseField("reason"); + public enum Status { SUCCESS, FAILURE } @@ -30,23 +37,30 @@ public interface Transform extends ToXContent { protected final Status status; @Nullable protected final Payload payload; @Nullable protected final String reason; + @Nullable protected final Exception exception; public Result(String type, Payload payload) { this.type = type; this.status = Status.SUCCESS; this.payload = payload; this.reason = null; + this.exception = null; + } + + public Result(String type, String reason) { + this.type = type; + this.status = Status.FAILURE; + this.reason = reason; + this.payload = null; + this.exception = null; } public Result(String type, Exception e) { - this(type, ExceptionsHelper.detailedMessage(e)); - } - - public Result(String type, String errorMessage) { this.type = type; this.status = Status.FAILURE; - this.reason = errorMessage; + this.reason = e.getMessage(); this.payload = null; + this.exception = e; } public String type() { @@ -70,16 +84,17 @@ public interface Transform extends ToXContent { @Override public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.field(Field.TYPE.getPreferredName(), type); - builder.field(Field.STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT)); + builder.field(TYPE.getPreferredName(), type); + builder.field(STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT)); switch (status) { case SUCCESS: - assert reason == null; - builder.field(Field.PAYLOAD.getPreferredName(), payload, params); + assert exception == null; + builder.field(PAYLOAD.getPreferredName(), payload, params); break; case FAILURE: assert payload == null; - builder.field(Field.REASON.getPreferredName(), reason); + builder.field(REASON.getPreferredName(), reason); + ElasticsearchException.generateFailureXContent(builder, params, exception, true); break; default: assert false; @@ -96,14 +111,4 @@ public interface Transform extends ToXContent { T build(); } - - interface Field { - ParseField TRANSFORM = new ParseField("transform"); - - ParseField TYPE = new ParseField("type"); - ParseField STATUS = new ParseField("status"); - ParseField PAYLOAD = new ParseField("payload"); - ParseField REASON = new ParseField("reason"); - - } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/chain/ChainTransform.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/chain/ChainTransform.java index ce48b2f1029..f210794d149 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/chain/ChainTransform.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/chain/ChainTransform.java @@ -162,7 +162,7 @@ public class ChainTransform implements Transform { } } - interface Field extends Transform.Field { + interface Field { ParseField RESULTS = new ParseField("results"); } } diff --git a/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/search/SearchTransform.java b/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/search/SearchTransform.java index d8c87b42c99..b51c305b35d 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/search/SearchTransform.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/watcher/transform/search/SearchTransform.java @@ -189,7 +189,7 @@ public class SearchTransform implements Transform { } } - public interface Field extends Transform.Field { + public interface Field { ParseField REQUEST = new ParseField("request"); ParseField TIMEOUT = new ParseField("timeout_in_millis"); ParseField TIMEOUT_HUMAN = new ParseField("timeout"); diff --git a/plugin/src/main/resources/monitoring/watches/elasticsearch_cluster_status.json b/plugin/src/main/resources/monitoring/watches/elasticsearch_cluster_status.json index 4f1cf9802f1..42771647eb4 100644 --- a/plugin/src/main/resources/monitoring/watches/elasticsearch_cluster_status.json +++ b/plugin/src/main/resources/monitoring/watches/elasticsearch_cluster_status.json @@ -138,7 +138,7 @@ }, "transform": { "script": { - "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.defaultAdminEmail : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def state = ctx.payload.check.hits.hits[0]._source.cluster_state.status;if (ctx.vars.not_resolved){ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check == false) {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = ['timestamp': ctx.execution_time, 'metadata': ctx.metadata.xpack];}if (ctx.vars.fails_check) {ctx.payload.prefix = 'Elasticsearch cluster status is ' + state + '.';if (state == 'red') {ctx.payload.message = 'Allocate missing primary shards and replica shards.';ctx.payload.metadata.severity = 2100;} else {ctx.payload.message = 'Allocate missing replica shards.';ctx.payload.metadata.severity = 1100;}}ctx.vars.state = state.toUpperCase();ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" + "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.default_admin_email : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def state = ctx.payload.check.hits.hits[0]._source.cluster_state.status;if (ctx.vars.not_resolved){ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check == false) {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = ['timestamp': ctx.execution_time, 'metadata': ctx.metadata.xpack];}if (ctx.vars.fails_check) {ctx.payload.prefix = 'Elasticsearch cluster status is ' + state + '.';if (state == 'red') {ctx.payload.message = 'Allocate missing primary shards and replica shards.';ctx.payload.metadata.severity = 2100;} else {ctx.payload.message = 'Allocate missing replica shards.';ctx.payload.metadata.severity = 1100;}}ctx.vars.state = state.toUpperCase();ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" } }, "actions": { diff --git a/plugin/src/main/resources/monitoring/watches/elasticsearch_version_mismatch.json b/plugin/src/main/resources/monitoring/watches/elasticsearch_version_mismatch.json index 5b285f5dc77..e787c168bfc 100644 --- a/plugin/src/main/resources/monitoring/watches/elasticsearch_version_mismatch.json +++ b/plugin/src/main/resources/monitoring/watches/elasticsearch_version_mismatch.json @@ -134,7 +134,7 @@ }, "transform": { "script": { - "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.defaultAdminEmail : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def versionMessage = null;if (ctx.vars.fails_check) {def versions = new ArrayList(ctx.payload.check.hits.hits[0]._source.cluster_stats.nodes.versions);Collections.sort(versions);versionMessage = 'Versions: [' + String.join(', ', versions) + '].';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check) {ctx.payload.message = versionMessage;} else {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = [ 'timestamp': ctx.execution_time, 'prefix': 'This cluster is running with multiple versions of Elasticsearch.', 'message': versionMessage, 'metadata': ctx.metadata.xpack ];}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" + "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.default_admin_email : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def versionMessage = null;if (ctx.vars.fails_check) {def versions = new ArrayList(ctx.payload.check.hits.hits[0]._source.cluster_stats.nodes.versions);Collections.sort(versions);versionMessage = 'Versions: [' + String.join(', ', versions) + '].';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check) {ctx.payload.message = versionMessage;} else {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = [ 'timestamp': ctx.execution_time, 'prefix': 'This cluster is running with multiple versions of Elasticsearch.', 'message': versionMessage, 'metadata': ctx.metadata.xpack ];}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" } }, "actions": { diff --git a/plugin/src/main/resources/monitoring/watches/kibana_version_mismatch.json b/plugin/src/main/resources/monitoring/watches/kibana_version_mismatch.json index eebf3561f1d..74c3319f5fe 100644 --- a/plugin/src/main/resources/monitoring/watches/kibana_version_mismatch.json +++ b/plugin/src/main/resources/monitoring/watches/kibana_version_mismatch.json @@ -161,7 +161,7 @@ }, "transform": { "script": { - "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.defaultAdminEmail : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def versionMessage = null;if (ctx.vars.fails_check) {versionMessage = 'Versions: [' + String.join(', ', ctx.vars.versions) + '].';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check) {ctx.payload.message = versionMessage;} else {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = [ 'timestamp': ctx.execution_time, 'prefix': 'This cluster is running with multiple versions of Kibana.', 'message': versionMessage, 'metadata': ctx.metadata.xpack ];}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" + "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.default_admin_email : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def versionMessage = null;if (ctx.vars.fails_check) {versionMessage = 'Versions: [' + String.join(', ', ctx.vars.versions) + '].';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check) {ctx.payload.message = versionMessage;} else {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = [ 'timestamp': ctx.execution_time, 'prefix': 'This cluster is running with multiple versions of Kibana.', 'message': versionMessage, 'metadata': ctx.metadata.xpack ];}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" } }, "actions": { diff --git a/plugin/src/main/resources/monitoring/watches/logstash_version_mismatch.json b/plugin/src/main/resources/monitoring/watches/logstash_version_mismatch.json index 5e67208cd77..64e082bcdde 100644 --- a/plugin/src/main/resources/monitoring/watches/logstash_version_mismatch.json +++ b/plugin/src/main/resources/monitoring/watches/logstash_version_mismatch.json @@ -161,7 +161,7 @@ }, "transform": { "script": { - "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.defaultAdminEmail : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def versionMessage = null;if (ctx.vars.fails_check) {versionMessage = 'Versions: [' + String.join(', ', ctx.vars.versions) + '].';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check) {ctx.payload.message = versionMessage;} else {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = [ 'timestamp': ctx.execution_time, 'prefix': 'This cluster is running with multiple versions of Logstash.', 'message': versionMessage, 'metadata': ctx.metadata.xpack ];}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" + "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.default_admin_email : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def versionMessage = null;if (ctx.vars.fails_check) {versionMessage = 'Versions: [' + String.join(', ', ctx.vars.versions) + '].';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;if (ctx.vars.fails_check) {ctx.payload.message = versionMessage;} else {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = [ 'timestamp': ctx.execution_time, 'prefix': 'This cluster is running with multiple versions of Logstash.', 'message': versionMessage, 'metadata': ctx.metadata.xpack ];}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" } }, "actions": { diff --git a/plugin/src/main/resources/monitoring/watches/xpack_license_expiration.json b/plugin/src/main/resources/monitoring/watches/xpack_license_expiration.json index 47787c94afc..32b22aa8488 100644 --- a/plugin/src/main/resources/monitoring/watches/xpack_license_expiration.json +++ b/plugin/src/main/resources/monitoring/watches/xpack_license_expiration.json @@ -127,7 +127,7 @@ }, "transform": { "script": { - "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.defaultAdminEmail : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def alertMessage = null;if (ctx.vars.fails_check) {alertMessage = 'Update your license.';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;ctx.payload.metadata = ctx.metadata.xpack;if (ctx.vars.fails_check == false) {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = ['timestamp': ctx.execution_time,'prefix': 'This cluster\\'s license is going to expire in {{#relativeTime}}metadata.time{{/relativeTime}} at {{#absoluteTime}}metadata.time{{/absoluteTime}}.','message': alertMessage,'metadata': ctx.metadata.xpack];}if (ctx.vars.fails_check) {ctx.payload.metadata.time = ctx.vars.expiry.toString();}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" + "source": "ctx.vars.email_recipient = (ctx.payload.kibana_settings.hits.total > 0) ? ctx.payload.kibana_settings.hits.hits[0]._source.kibana_settings.xpack.default_admin_email : null;ctx.vars.is_new = ctx.vars.fails_check && !ctx.vars.not_resolved;ctx.vars.is_resolved = !ctx.vars.fails_check && ctx.vars.not_resolved;def alertMessage = null;if (ctx.vars.fails_check) {alertMessage = 'Update your license.';}if (ctx.vars.not_resolved) {ctx.payload = ctx.payload.alert.hits.hits[0]._source;ctx.payload.metadata = ctx.metadata.xpack;if (ctx.vars.fails_check == false) {ctx.payload.resolved_timestamp = ctx.execution_time;}} else {ctx.payload = ['timestamp': ctx.execution_time,'prefix': 'This cluster\\'s license is going to expire in {{#relativeTime}}metadata.time{{/relativeTime}} at {{#absoluteTime}}metadata.time{{/absoluteTime}}.','message': alertMessage,'metadata': ctx.metadata.xpack];}if (ctx.vars.fails_check) {ctx.payload.metadata.time = ctx.vars.expiry.toString();}ctx.payload.update_timestamp = ctx.execution_time;return ctx.payload;" } }, "actions": { diff --git a/plugin/src/main/resources/watch-history.json b/plugin/src/main/resources/watch-history.json index dfad3960b78..a7dda248c02 100644 --- a/plugin/src/main/resources/watch-history.json +++ b/plugin/src/main/resources/watch-history.json @@ -30,6 +30,16 @@ } } }, + { + "disabled_exception_fields": { + "path_match": "result\\.(input(\\..+)*|(transform(\\..+)*)|(actions\\.transform(\\..+)*)|actions)\\.error", + "match_pattern": "regex", + "mapping": { + "type": "object", + "enabled": false + } + } + }, { "disabled_jira_custom_fields": { "path_match": "result.actions.jira.fields.customfield_*", diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ml/integration/BasicDistributedJobsIT.java b/plugin/src/test/java/org/elasticsearch/xpack/ml/integration/BasicDistributedJobsIT.java index d2fe5c3efee..9eb70508e91 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ml/integration/BasicDistributedJobsIT.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ml/integration/BasicDistributedJobsIT.java @@ -215,6 +215,7 @@ public class BasicDistributedJobsIT extends BaseMlIntegTestCase { DiscoveryNode node = clusterState.nodes().resolveNode(task.getExecutorNode()); Map expectedNodeAttr = new HashMap<>(); expectedNodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true"); + expectedNodeAttr.put(MachineLearning.MAX_OPEN_JOBS_NODE_ATTR, "10"); assertEquals(expectedNodeAttr, node.getAttributes()); JobTaskStatus jobTaskStatus = (JobTaskStatus) task.getStatus(); assertNotNull(jobTaskStatus); @@ -402,6 +403,7 @@ public class BasicDistributedJobsIT extends BaseMlIntegTestCase { DiscoveryNode node = clusterState.nodes().resolveNode(task.getExecutorNode()); Map expectedNodeAttr = new HashMap<>(); expectedNodeAttr.put(MachineLearning.ML_ENABLED_NODE_ATTR, "true"); + expectedNodeAttr.put(MachineLearning.MAX_OPEN_JOBS_NODE_ATTR, "10"); assertEquals(expectedNodeAttr, node.getAttributes()); JobTaskStatus jobTaskStatus = (JobTaskStatus) task.getStatus(); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ml/job/config/JobTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ml/job/config/JobTests.java index 17238e675f5..35994b02ef1 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ml/job/config/JobTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ml/job/config/JobTests.java @@ -470,15 +470,17 @@ public class JobTests extends AbstractSerializingTestCase { public void testEmptyGroup() { Job.Builder builder = buildJobBuilder("foo"); + builder.setGroups(Arrays.asList("foo-group", "")); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> builder.setGroups(Arrays.asList("foo-group", ""))); + () -> builder.build()); assertThat(e.getMessage(), containsString("Invalid group id ''")); } public void testInvalidGroup() { Job.Builder builder = buildJobBuilder("foo"); + builder.setGroups(Arrays.asList("foo-group", "$$$")); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> builder.setGroups(Arrays.asList("foo-group", "$$$"))); + () -> builder.build()); assertThat(e.getMessage(), containsString("Invalid group id '$$$'")); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessorTests.java b/plugin/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessorTests.java index 94785055cf5..70cfb6f8262 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessorTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/ml/job/process/autodetect/output/StateProcessorTests.java @@ -26,6 +26,7 @@ import java.util.List; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -51,13 +52,13 @@ public class StateProcessorTests extends ESTestCase { private static final int LARGE_DOC_SIZE = 1000000; private Client client; - private ActionFuture bulkResponseFuture; private StateProcessor stateProcessor; @Before public void initialize() throws IOException { client = mock(Client.class); - bulkResponseFuture = mock(ActionFuture.class); + @SuppressWarnings("unchecked") + ActionFuture bulkResponseFuture = mock(ActionFuture.class); stateProcessor = spy(new StateProcessor(Settings.EMPTY, client)); when(client.bulk(any(BulkRequest.class))).thenReturn(bulkResponseFuture); } @@ -87,12 +88,12 @@ public class StateProcessorTests extends ESTestCase { stateProcessor.process("_id", stream); - verify(stateProcessor, times(6)).persist(eq("_id"), any()); + verify(stateProcessor, never()).persist(eq("_id"), any()); Mockito.verifyNoMoreInteractions(client); } public void testStateReadGivenConsecutiveSpacesFollowedByZeroByte() throws IOException { - String zeroBytes = " \0"; + String zeroBytes = " \n\0"; ByteArrayInputStream stream = new ByteArrayInputStream(zeroBytes.getBytes(StandardCharsets.UTF_8)); stateProcessor.process("_id", stream); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java index 3599f4e03ce..8fbe69c7377 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/MonitoringSettingsIntegTests.java @@ -27,7 +27,6 @@ import static org.hamcrest.Matchers.equalTo; public class MonitoringSettingsIntegTests extends MonitoringIntegTestCase { private final TimeValue interval = newRandomTimeValue(); private final TimeValue indexStatsTimeout = newRandomTimeValue(); - private final TimeValue indicesStatsTimeout = newRandomTimeValue(); private final String[] indices = randomStringArray(); private final TimeValue clusterStateTimeout = newRandomTimeValue(); private final TimeValue clusterStatsTimeout = newRandomTimeValue(); @@ -55,7 +54,6 @@ public class MonitoringSettingsIntegTests extends MonitoringIntegTestCase { return Settings.builder() .put(MonitoringSettings.INTERVAL.getKey(), interval) .put(MonitoringSettings.INDEX_STATS_TIMEOUT.getKey(), indexStatsTimeout) - .put(MonitoringSettings.INDICES_STATS_TIMEOUT.getKey(), indicesStatsTimeout) .putArray(MonitoringSettings.INDICES.getKey(), indices) .put(MonitoringSettings.CLUSTER_STATE_TIMEOUT.getKey(), clusterStateTimeout) .put(MonitoringSettings.CLUSTER_STATS_TIMEOUT.getKey(), clusterStatsTimeout) @@ -68,7 +66,6 @@ public class MonitoringSettingsIntegTests extends MonitoringIntegTestCase { public void testMonitoringSettings() throws Exception { for (final MonitoringSettings monitoringSettings : internalCluster().getInstances(MonitoringSettings.class)) { assertThat(monitoringSettings.indexStatsTimeout().millis(), equalTo(indexStatsTimeout.millis())); - assertThat(monitoringSettings.indicesStatsTimeout().millis(), equalTo(indicesStatsTimeout.millis())); assertArrayEquals(monitoringSettings.indices(), indices); assertThat(monitoringSettings.clusterStateTimeout().millis(), equalTo(clusterStateTimeout.millis())); assertThat(monitoringSettings.clusterStatsTimeout().millis(), equalTo(clusterStatsTimeout.millis())); @@ -89,7 +86,6 @@ public class MonitoringSettingsIntegTests extends MonitoringIntegTestCase { MonitoringSettings.INTERVAL, MonitoringSettings.INDEX_RECOVERY_TIMEOUT, MonitoringSettings.INDEX_STATS_TIMEOUT, - MonitoringSettings.INDICES_STATS_TIMEOUT, MonitoringSettings.INDEX_RECOVERY_ACTIVE_ONLY, MonitoringSettings.CLUSTER_STATE_TIMEOUT, MonitoringSettings.CLUSTER_STATS_TIMEOUT, @@ -129,8 +125,6 @@ public class MonitoringSettingsIntegTests extends MonitoringIntegTestCase { for (final MonitoringSettings monitoringSettings1 : internalCluster().getInstances(MonitoringSettings.class)) { if (setting == MonitoringSettings.INDEX_STATS_TIMEOUT) { assertEquals(monitoringSettings1.indexStatsTimeout(), setting.get(updatedSettings)); - } else if (setting == MonitoringSettings.INDICES_STATS_TIMEOUT) { - assertEquals(monitoringSettings1.indicesStatsTimeout(), setting.get(updatedSettings)); } else if (setting == MonitoringSettings.CLUSTER_STATS_TIMEOUT) { assertEquals(monitoringSettings1.clusterStatsTimeout(), setting.get(updatedSettings)); } else if (setting == MonitoringSettings.JOB_STATS_TIMEOUT) { diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java index 6c7f75e1438..8a41a8cebe4 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.monitoring.collector.indices; import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.stats.IndexStats; +import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; @@ -19,8 +20,12 @@ import org.elasticsearch.xpack.monitoring.exporter.MonitoringDoc; import java.util.Collection; import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; +import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -40,19 +45,19 @@ public class IndexStatsCollectorTests extends AbstractCollectorTestCase { public void testEmptyCluster() throws Exception { final String node = internalCluster().startNode(); waitForNoBlocksOnNode(node); - assertThat(newIndexStatsCollector(node).doCollect(), hasSize(0)); + assertThat(newIndexStatsCollector(node).doCollect(), hasSize(1)); } public void testEmptyClusterAllIndices() throws Exception { final String node = internalCluster().startNode(Settings.builder().put(MonitoringSettings.INDICES.getKey(), MetaData.ALL)); waitForNoBlocksOnNode(node); - assertThat(newIndexStatsCollector(node).doCollect(), hasSize(0)); + assertThat(newIndexStatsCollector(node).doCollect(), hasSize(1)); } public void testEmptyClusterMissingIndex() throws Exception { final String node = internalCluster().startNode(Settings.builder().put(MonitoringSettings.INDICES.getKey(), "unknown")); waitForNoBlocksOnNode(node); - assertThat(newIndexStatsCollector(node).doCollect(), hasSize(0)); + assertThat(newIndexStatsCollector(node).doCollect(), hasSize(1)); } public void testIndexStatsCollectorOneIndex() throws Exception { @@ -68,19 +73,40 @@ public class IndexStatsCollectorTests extends AbstractCollectorTestCase { client().prepareIndex(indexName, "test").setSource("num", i).get(); } - flush(); refresh(); assertHitCount(client().prepareSearch().setSize(0).get(), nbDocs); Collection results = newIndexStatsCollector().doCollect(); - assertThat(results, hasSize(1)); + assertThat(results, hasSize(2)); - MonitoringDoc monitoringDoc = results.iterator().next(); - assertNotNull(monitoringDoc); - assertThat(monitoringDoc, instanceOf(IndexStatsMonitoringDoc.class)); + // indices stats + final Optional indicesStatsDoc = + results.stream().filter(doc -> doc instanceof IndicesStatsMonitoringDoc).map(doc -> (IndicesStatsMonitoringDoc)doc).findFirst(); - IndexStatsMonitoringDoc indexStatsMonitoringDoc = (IndexStatsMonitoringDoc) monitoringDoc; + assertThat(indicesStatsDoc.isPresent(), is(true)); + + IndicesStatsMonitoringDoc indicesStatsMonitoringDoc = indicesStatsDoc.get(); + assertThat(indicesStatsMonitoringDoc.getClusterUUID(), equalTo(client().admin().cluster(). + prepareState().setMetaData(true).get().getState().metaData().clusterUUID())); + assertThat(indicesStatsMonitoringDoc.getTimestamp(), greaterThan(0L)); + assertThat(indicesStatsMonitoringDoc.getSourceNode(), notNullValue()); + + IndicesStatsResponse indicesStats = indicesStatsMonitoringDoc.getIndicesStats(); + assertNotNull(indicesStats); + assertThat(indicesStats.getIndices().keySet(), hasSize(1)); + assertThat(indicesStats.getIndex(indexName).getShards(), arrayWithSize(getNumShards(indexName).totalNumShards)); + + // index stats + final Optional indexStatsDoc = + results.stream() + .filter(doc -> doc instanceof IndexStatsMonitoringDoc) + .map(doc -> (IndexStatsMonitoringDoc)doc) + .findFirst(); + + assertThat(indexStatsDoc.isPresent(), is(true)); + + IndexStatsMonitoringDoc indexStatsMonitoringDoc = indexStatsDoc.get(); assertThat(indexStatsMonitoringDoc.getMonitoringId(), equalTo(MonitoredSystem.ES.getSystem())); assertThat(indexStatsMonitoringDoc.getMonitoringVersion(), equalTo(Version.CURRENT.toString())); assertThat(indexStatsMonitoringDoc.getClusterUUID(), @@ -118,7 +144,6 @@ public class IndexStatsCollectorTests extends AbstractCollectorTestCase { } } - flush(); refresh(); for (int i = 0; i < nbIndices; i++) { @@ -128,18 +153,45 @@ public class IndexStatsCollectorTests extends AbstractCollectorTestCase { String clusterUUID = client().admin().cluster().prepareState().setMetaData(true).get().getState().metaData().clusterUUID(); Collection results = newIndexStatsCollector().doCollect(); - assertThat(results, hasSize(nbIndices)); + // extra document is for the IndicesStatsMonitoringDoc + assertThat(results, hasSize(nbIndices + 1)); + + // indices stats + final Optional indicesStatsDoc = + results.stream() + .filter(doc -> doc instanceof IndicesStatsMonitoringDoc) + .map(doc -> (IndicesStatsMonitoringDoc)doc) + .findFirst(); + + assertThat(indicesStatsDoc.isPresent(), is(true)); + + IndicesStatsMonitoringDoc indicesStatsMonitoringDoc = indicesStatsDoc.get(); + assertThat(indicesStatsMonitoringDoc.getMonitoringId(), equalTo(MonitoredSystem.ES.getSystem())); + assertThat(indicesStatsMonitoringDoc.getMonitoringVersion(), equalTo(Version.CURRENT.toString())); + assertThat(indicesStatsMonitoringDoc.getClusterUUID(), + equalTo(client().admin().cluster().prepareState().setMetaData(true).get().getState().metaData().clusterUUID())); + assertThat(indicesStatsMonitoringDoc.getTimestamp(), greaterThan(0L)); + + IndicesStatsResponse indicesStats = indicesStatsMonitoringDoc.getIndicesStats(); + assertNotNull(indicesStats); + assertThat(indicesStats.getIndices().keySet(), hasSize(nbIndices)); + + // index stats + final List indexStatsDocs = + results.stream() + .filter(doc -> doc instanceof IndexStatsMonitoringDoc) + .map(doc -> (IndexStatsMonitoringDoc)doc) + .collect(Collectors.toList()); + + assertThat(indexStatsDocs, hasSize(nbIndices)); for (int i = 0; i < nbIndices; i++) { String indexName = indexPrefix + i; boolean found = false; - Iterator it = results.iterator(); + Iterator it = indexStatsDocs.iterator(); while (!found && it.hasNext()) { - MonitoringDoc monitoringDoc = it.next(); - assertThat(monitoringDoc, instanceOf(IndexStatsMonitoringDoc.class)); - - IndexStatsMonitoringDoc indexStatsMonitoringDoc = (IndexStatsMonitoringDoc) monitoringDoc; + IndexStatsMonitoringDoc indexStatsMonitoringDoc = it.next(); IndexStats indexStats = indexStatsMonitoringDoc.getIndexStats(); assertNotNull(indexStats); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsCollectorTests.java b/plugin/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsCollectorTests.java deleted file mode 100644 index 09844ffb411..00000000000 --- a/plugin/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndicesStatsCollectorTests.java +++ /dev/null @@ -1,156 +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.monitoring.collector.indices; - -import org.elasticsearch.Version; -import org.elasticsearch.action.admin.indices.stats.IndexStats; -import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; -import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.XPackLicenseState; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; -import org.elasticsearch.xpack.monitoring.MonitoredSystem; -import org.elasticsearch.xpack.monitoring.MonitoringSettings; -import org.elasticsearch.xpack.monitoring.collector.AbstractCollectorTestCase; -import org.elasticsearch.xpack.monitoring.exporter.MonitoringDoc; -import org.hamcrest.Matchers; - -import java.util.Collection; - -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.notNullValue; - -@ClusterScope(numDataNodes = 0, numClientNodes = 0, transportClientRatio = 0.0) -public class IndicesStatsCollectorTests extends AbstractCollectorTestCase { - - @Override - protected int numberOfReplicas() { - return 0; - } - - public void testEmptyCluster() throws Exception { - final String node = internalCluster().startNode(); - waitForNoBlocksOnNode(node); - assertThat(newIndicesStatsCollector(node).doCollect(), hasSize(1)); - } - - public void testEmptyClusterAllIndices() throws Exception { - final String node = internalCluster().startNode(Settings.builder().put(MonitoringSettings.INDICES.getKey(), MetaData.ALL)); - waitForNoBlocksOnNode(node); - assertThat(newIndicesStatsCollector(node).doCollect(), hasSize(1)); - } - - public void testEmptyClusterMissingIndex() throws Exception { - final String node = internalCluster().startNode(Settings.builder().put(MonitoringSettings.INDICES.getKey(), "unknown")); - waitForNoBlocksOnNode(node); - assertThat(newIndicesStatsCollector(node).doCollect(), hasSize(1)); - } - - public void testIndicesStatsCollectorOneIndex() throws Exception { - final String node = internalCluster().startNode(); - waitForNoBlocksOnNode(node); - - final String indexName = "one-index"; - createIndex(indexName); - ensureGreen(indexName); - - - final int nbDocs = randomIntBetween(1, 20); - for (int i = 0; i < nbDocs; i++) { - client().prepareIndex(indexName, "test").setSource("num", i).get(); - } - - flush(); - refresh(); - - assertHitCount(client().prepareSearch().setSize(0).get(), nbDocs); - - Collection results = newIndicesStatsCollector().doCollect(); - assertThat(results, hasSize(1)); - - MonitoringDoc monitoringDoc = results.iterator().next(); - assertThat(monitoringDoc, instanceOf(IndicesStatsMonitoringDoc.class)); - - IndicesStatsMonitoringDoc indicesStatsMonitoringDoc = (IndicesStatsMonitoringDoc) monitoringDoc; - assertThat(indicesStatsMonitoringDoc.getClusterUUID(), equalTo(client().admin().cluster(). - prepareState().setMetaData(true).get().getState().metaData().clusterUUID())); - assertThat(indicesStatsMonitoringDoc.getTimestamp(), greaterThan(0L)); - assertThat(indicesStatsMonitoringDoc.getSourceNode(), notNullValue()); - - IndicesStatsResponse indicesStats = indicesStatsMonitoringDoc.getIndicesStats(); - assertNotNull(indicesStats); - assertThat(indicesStats.getIndices().keySet(), hasSize(1)); - - IndexStats indexStats = indicesStats.getIndex(indexName); - assertThat(indexStats.getShards(), Matchers.arrayWithSize(getNumShards(indexName).totalNumShards)); - } - - public void testIndicesStatsCollectorMultipleIndices() throws Exception { - final String node = internalCluster().startNode(); - waitForNoBlocksOnNode(node); - - final String indexPrefix = "multi-indices-"; - final int nbIndices = randomIntBetween(1, 5); - int[] docsPerIndex = new int[nbIndices]; - - for (int i = 0; i < nbIndices; i++) { - String index = indexPrefix + i; - createIndex(index); - ensureGreen(index); - - docsPerIndex[i] = randomIntBetween(1, 20); - for (int j = 0; j < docsPerIndex[i]; j++) { - client().prepareIndex(index, "test").setSource("num", i).get(); - } - } - - flush(); - refresh(); - - for (int i = 0; i < nbIndices; i++) { - assertHitCount(client().prepareSearch(indexPrefix + i).setSize(0).get(), docsPerIndex[i]); - } - - Collection results = newIndicesStatsCollector().doCollect(); - assertThat(results, hasSize(1)); - - MonitoringDoc monitoringDoc = results.iterator().next(); - assertThat(monitoringDoc, instanceOf(IndicesStatsMonitoringDoc.class)); - - IndicesStatsMonitoringDoc indicesStatsMonitoringDoc = (IndicesStatsMonitoringDoc) monitoringDoc; - assertThat(indicesStatsMonitoringDoc.getMonitoringId(), equalTo(MonitoredSystem.ES.getSystem())); - assertThat(indicesStatsMonitoringDoc.getMonitoringVersion(), equalTo(Version.CURRENT.toString())); - assertThat(indicesStatsMonitoringDoc.getClusterUUID(), - equalTo(client().admin().cluster().prepareState().setMetaData(true).get().getState().metaData().clusterUUID())); - assertThat(indicesStatsMonitoringDoc.getTimestamp(), greaterThan(0L)); - - IndicesStatsResponse indicesStats = indicesStatsMonitoringDoc.getIndicesStats(); - assertNotNull(indicesStats); - assertThat(indicesStats.getIndices().keySet(), hasSize(nbIndices)); - } - - private IndicesStatsCollector newIndicesStatsCollector() { - // This collector runs on master node only - return newIndicesStatsCollector(internalCluster().getMasterName()); - } - - private IndicesStatsCollector newIndicesStatsCollector(String nodeId) { - if (!Strings.hasText(nodeId)) { - nodeId = randomFrom(internalCluster().getNodeNames()); - } - return new IndicesStatsCollector(internalCluster().getInstance(Settings.class, nodeId), - internalCluster().getInstance(ClusterService.class, nodeId), - internalCluster().getInstance(MonitoringSettings.class, nodeId), - internalCluster().getInstance(XPackLicenseState.class, nodeId), - securedClient(nodeId)); - } -} diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java index 38be35f3aa4..2522def9f6d 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/email/EmailActionTests.java @@ -553,9 +553,9 @@ public class EmailActionTests extends ESTestCase { .buildMock(); Action.Result result = executableEmailAction.execute("test", ctx, new Payload.Simple()); - assertThat(result, instanceOf(EmailAction.Result.Failure.class)); - EmailAction.Result.Failure failure = (EmailAction.Result.Failure) result; - assertThat(failure.reason(), + assertThat(result, instanceOf(EmailAction.Result.FailureWithException.class)); + EmailAction.Result.FailureWithException failure = (EmailAction.Result.FailureWithException) result; + assertThat(failure.getException().getMessage(), is("Watch[watch1] attachment[second] HTTP error status host[localhost], port[80], method[GET], path[/second], " + "status[403]")); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionTests.java index 17a2135a19e..c5338edc6bd 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionTests.java @@ -110,9 +110,10 @@ public class SlackActionTests extends ESTestCase { for (int i = 0; i < count; i++) { HttpResponse response = mock(HttpResponse.class); HttpRequest request = mock(HttpRequest.class); - switch (randomIntBetween(0, 2)) { + int randomInt = randomIntBetween(0, 2); + switch (randomInt) { case 0: - messages.add(SentMessages.SentMessage.error(randomAlphaOfLength(10), message, "unknown error")); + messages.add(SentMessages.SentMessage.error(randomAlphaOfLength(10), message, new Exception("unknown error"))); hasError = true; break; case 1: diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java index afbc70a0ac2..51c8caf909f 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/execution/ExecutionServiceTests.java @@ -45,6 +45,7 @@ import org.elasticsearch.xpack.watcher.watch.WatchStatus; import org.joda.time.DateTime; import org.junit.Before; +import java.io.IOException; import java.time.Clock; import java.util.ArrayList; import java.util.Arrays; @@ -229,7 +230,7 @@ public class ExecutionServiceTests extends ESTestCase { input = mock(ExecutableInput.class); Input.Result inputResult = mock(Input.Result.class); when(inputResult.status()).thenReturn(Input.Result.Status.FAILURE); - when(inputResult.reason()).thenReturn("_reason"); + when(inputResult.getException()).thenReturn(new IOException()); when(input.execute(eq(context), any(Payload.class))).thenReturn(inputResult); Condition.Result conditionResult = AlwaysCondition.RESULT_INSTANCE; diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java index 6122783372f..1cf5636708f 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/history/HistoryTemplateHttpMappingsTests.java @@ -5,7 +5,12 @@ */ package org.elasticsearch.xpack.watcher.history; +import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.cluster.metadata.MappingMetaData; +import org.elasticsearch.common.collect.ImmutableOpenMap; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.test.http.MockResponse; @@ -15,20 +20,31 @@ import org.elasticsearch.xpack.common.http.HttpMethod; import org.elasticsearch.xpack.common.http.HttpRequestTemplate; import org.elasticsearch.xpack.watcher.condition.AlwaysCondition; import org.elasticsearch.xpack.watcher.execution.ExecutionState; +import org.elasticsearch.xpack.watcher.support.xcontent.ObjectPath; import org.elasticsearch.xpack.watcher.test.AbstractWatcherIntegrationTestCase; import org.elasticsearch.xpack.watcher.transport.actions.put.PutWatchResponse; import org.junit.After; import org.junit.Before; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.xpack.watcher.actions.ActionBuilders.webhookAction; import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder; import static org.elasticsearch.xpack.watcher.input.InputBuilders.httpInput; import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule; import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.interval; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; /** @@ -111,4 +127,64 @@ public class HistoryTemplateHttpMappingsTests extends AbstractWatcherIntegration assertThat(webServer.requests().get(0).getUri().getPath(), is("/input/path")); assertThat(webServer.requests().get(1).getUri().getPath(), is("/webhook/path")); } + + public void testExceptionMapping() { + // delete all history indices to ensure that we start with a fresh mapping + assertAcked(client().admin().indices().prepareDelete(HistoryStore.INDEX_PREFIX + "*")); + + String id = randomAlphaOfLength(10); + // switch between delaying the input or the action http request + boolean abortAtInput = randomBoolean(); + if (abortAtInput) { + webServer.enqueue(new MockResponse().setBeforeReplyDelay(TimeValue.timeValueSeconds(5))); + } else { + webServer.enqueue(new MockResponse().setBody("{}")); + webServer.enqueue(new MockResponse().setBeforeReplyDelay(TimeValue.timeValueSeconds(5))); + } + + PutWatchResponse putWatchResponse = watcherClient().preparePutWatch(id).setSource(watchBuilder() + .trigger(schedule(interval("5s"))) + .input(httpInput(HttpRequestTemplate.builder("localhost", webServer.getPort()) + .path("/") + .readTimeout(TimeValue.timeValueMillis(10)))) + .condition(AlwaysCondition.INSTANCE) + .addAction("_webhook", webhookAction(HttpRequestTemplate.builder("localhost", webServer.getPort()) + .readTimeout(TimeValue.timeValueMillis(10)) + .path("/webhook/path") + .method(HttpMethod.POST) + .body("_body")))) + .get(); + + assertThat(putWatchResponse.isCreated(), is(true)); + watcherClient().prepareExecuteWatch(id).setRecordExecution(true).get(); + + // ensure watcher history index has been written with this id + flushAndRefresh(HistoryStore.INDEX_PREFIX + "*"); + SearchResponse searchResponse = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*") + .setQuery(QueryBuilders.termQuery("watch_id", id)) + .get(); + assertHitCount(searchResponse, 1L); + + // ensure that enabled is set to false + List indexed = new ArrayList<>(); + GetMappingsResponse mappingsResponse = client().admin().indices().prepareGetMappings(HistoryStore.INDEX_PREFIX + "*").get(); + Iterator> iterator = mappingsResponse.getMappings().valuesIt(); + while (iterator.hasNext()) { + ImmutableOpenMap mapping = iterator.next(); + assertThat(mapping.containsKey("doc"), is(true)); + Map docMapping = mapping.get("doc").getSourceAsMap(); + if (abortAtInput) { + Boolean enabled = ObjectPath.eval("properties.result.properties.input.properties.error.enabled", docMapping); + indexed.add(enabled); + } else { + Boolean enabled = ObjectPath.eval("properties.result.properties.actions.properties.error.enabled", docMapping); + indexed.add(enabled); + } + } + + assertThat(indexed, hasSize(greaterThanOrEqualTo(1))); + logger.info("GOT [{}]", indexed); + assertThat(indexed, hasItem(false)); + assertThat(indexed, not(hasItem(true))); + } } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/chain/ChainInputTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/chain/ChainInputTests.java index 4297cbec901..a79e526c52c 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/chain/ChainInputTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/chain/ChainInputTests.java @@ -55,6 +55,7 @@ import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; import static org.joda.time.DateTimeZone.UTC; public class ChainInputTests extends ESTestCase { @@ -165,7 +166,8 @@ public class ChainInputTests extends ESTestCase { XContentBuilder builder = jsonBuilder(); chainedResult.toXContent(builder, ToXContent.EMPTY_PARAMS); - assertThat(builder.bytes().utf8ToString(), containsString("\"reason\":\"ElasticsearchException[foo]\"")); + assertThat(builder.bytes().utf8ToString(), containsString("\"type\":\"exception\"")); + assertThat(builder.bytes().utf8ToString(), containsString("\"reason\":\"foo\"")); } /* https://github.com/elastic/x-plugins/issues/3736 diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java index a53e023649c..41bf9762561 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/input/http/HttpInputTests.java @@ -10,7 +10,10 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +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.common.xcontent.XContentType; import org.elasticsearch.test.ESTestCase; @@ -33,6 +36,7 @@ import org.elasticsearch.xpack.watcher.execution.WatchExecutionContext; import org.elasticsearch.xpack.watcher.input.InputBuilders; import org.elasticsearch.xpack.watcher.input.simple.ExecutableSimpleInput; import org.elasticsearch.xpack.watcher.input.simple.SimpleInput; +import org.elasticsearch.xpack.watcher.support.xcontent.ObjectPath; import org.elasticsearch.xpack.watcher.trigger.schedule.IntervalSchedule; import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTrigger; import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleTriggerEvent; @@ -42,6 +46,7 @@ import org.elasticsearch.xpack.watcher.watch.WatchStatus; import org.joda.time.DateTime; import org.junit.Before; +import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; @@ -60,7 +65,9 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.startsWith; import static org.joda.time.DateTimeZone.UTC; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; @@ -325,6 +332,32 @@ public class HttpInputTests extends ESTestCase { assertThat(data.get(1).get("foo"), is("second")); } + public void testExceptionCase() throws Exception { + when(httpClient.execute(any(HttpRequest.class))).thenThrow(new IOException("could not connect")); + + HttpRequestTemplate.Builder request = HttpRequestTemplate.builder("localhost", 8080); + HttpInput httpInput = InputBuilders.httpInput(request.build()).build(); + ExecutableHttpInput input = new ExecutableHttpInput(httpInput, logger, httpClient, templateEngine); + + WatchExecutionContext ctx = createWatchExecutionContext(); + HttpInput.Result result = input.execute(ctx, new Payload.Simple()); + + assertThat(result.getException(), is(notNullValue())); + assertThat(result.getException(), is(instanceOf(IOException.class))); + assertThat(result.getException().getMessage(), is("could not connect")); + + try (XContentBuilder builder = jsonBuilder()) { + result.toXContent(builder, ToXContent.EMPTY_PARAMS); + BytesReference bytes = builder.bytes(); + try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(NamedXContentRegistry.EMPTY, bytes)) { + Map data = parser.map(); + String reason = ObjectPath.eval("error.reason", data); + assertThat(reason, is("could not connect")); + String type = ObjectPath.eval("error.type", data); + assertThat(type, is("i_o_exception")); + } + } + } private WatchExecutionContext createWatchExecutionContext() { Watch watch = new Watch("test-watch", diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HipChatServiceTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HipChatServiceTests.java index bc96fdd51e0..85a3a7c74df 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HipChatServiceTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HipChatServiceTests.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.watcher.test.integration; +import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.junit.annotations.Network; @@ -201,7 +202,10 @@ public class HipChatServiceTests extends AbstractWatcherIntegrationTestCase { for (SentMessages.SentMessage message : messages) { logger.info("Request: [{}]", message.getRequest()); logger.info("Response: [{}]", message.getResponse()); - assertThat("Expected no failures, but got [" + message.getFailureReason() + "]", message.successful(), is(true)); + if (message.getException() != null) { + logger.info("Exception stacktrace: [{}]", ExceptionsHelper.stackTrace(message.getException())); + } + assertThat(message.isSuccess(), is(true)); assertThat(message.getRequest(), notNullValue()); assertThat(message.getResponse(), notNullValue()); assertThat(message.getResponse().status(), lessThan(300)); diff --git a/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/SlackServiceTests.java b/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/SlackServiceTests.java index bfa4e47dc55..36b643708cb 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/SlackServiceTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/watcher/test/integration/SlackServiceTests.java @@ -38,6 +38,7 @@ import static org.elasticsearch.xpack.watcher.trigger.schedule.Schedules.interva import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; @Network public class SlackServiceTests extends AbstractWatcherIntegrationTestCase { @@ -78,7 +79,7 @@ public class SlackServiceTests extends AbstractWatcherIntegrationTestCase { assertThat(messages.count(), is(2)); for (SentMessages.SentMessage sentMessage : messages) { try { - assertThat(sentMessage.successful(), is(true)); + assertThat(sentMessage.getException(), is(nullValue())); assertThat(sentMessage.getRequest(), notNullValue()); assertThat(sentMessage.getResponse(), notNullValue()); assertThat(sentMessage.getResponse().status(), lessThan(300)); diff --git a/plugin/src/test/resources/rest-api-spec/test/watcher/execute_watch/20_transform.yml b/plugin/src/test/resources/rest-api-spec/test/watcher/execute_watch/20_transform.yml index 9325326ebd8..8958c02a727 100644 --- a/plugin/src/test/resources/rest-api-spec/test/watcher/execute_watch/20_transform.yml +++ b/plugin/src/test/resources/rest-api-spec/test/watcher/execute_watch/20_transform.yml @@ -192,4 +192,5 @@ setup: - match: { watch_record.trigger_event.type: "manual" } - match: { watch_record.state: "executed" } - match: { watch_record.result.transform.status: "failure" } - - match: { watch_record.result.transform.reason: "ParsingException[no [query] registered for [does_not_exist]]" } + - match: { watch_record.result.transform.reason: "no [query] registered for [does_not_exist]" } + - is_true: watch_record.result.transform.error