Refactoring: Introduced TriggeredWatch concept.
Before WatchRecord was used to keep track of an execution of a Watch and used to store actual end results to it before sealing it. (for example build dashboard on the history indices) The keeping track of an execution has been moved from WatchRecord to TriggeredWatch. If a watch triggers a TriggeredWatch is stored. The TriggeredWatch has its own index and only the watch_id (is part of id), trigger event and state is stored. If the execution of a Watch has finished (regardless if it was successfully) a WatchRecord is stored in a history index and the TriggeredWatch is deleted. When a watch is getting executedtThe triggered watch is used the create the watch context. Also the WatchRecord.State has been removed to its own enum class named ExecutionState. The CHECKING value has been removed, because it wasn't really used. The CHECKING state was set when the execution began, but it was never persisted and because of this state has also been removed from triggered watch. By separating the result of a watch execution we are more flexible to in the future change the document format of WatchRecord. The history indices will be used by users to build analytics on top of watcher. Also the history indices become truely append only indices. When update the watch status, only change the status part with the update api Also set the version when we delete the watch on the in memory instance enforce more ensureStarted() in the components Removed all watch record and result parsing code (actions, conditions, inputs and transforms) Original commit: elastic/x-pack-elasticsearch@8f5ffdac13
This commit is contained in:
parent
6175b9efda
commit
4335669635
|
@ -73,12 +73,12 @@ public class WatcherService extends AbstractComponent {
|
|||
if (state.compareAndSet(WatcherState.STARTED, WatcherState.STOPPING)) {
|
||||
logger.info("stopping watch service...");
|
||||
triggerService.stop();
|
||||
executionService.stop();
|
||||
try {
|
||||
watchLockService.stop();
|
||||
} catch (WatchLockService.TimeoutException we) {
|
||||
logger.warn("error stopping WatchLockService", we);
|
||||
}
|
||||
executionService.stop();
|
||||
watchStore.stop();
|
||||
state.set(WatcherState.STOPPED);
|
||||
logger.info("watch service has stopped");
|
||||
|
|
|
@ -7,7 +7,6 @@ package org.elasticsearch.watcher.actions;
|
|||
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -29,8 +28,6 @@ public abstract class ActionFactory<A extends Action, E extends ExecutableAction
|
|||
|
||||
public abstract A parseAction(String watchId, String actionId, XContentParser parser) throws IOException;
|
||||
|
||||
public abstract Action.Result parseResult(Wid wid, String actionId, XContentParser parser) throws IOException;
|
||||
|
||||
public abstract E createExecutable(A action);
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.elasticsearch.watcher.actions;
|
|||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.license.LicenseService;
|
||||
import org.elasticsearch.watcher.support.validation.Validation;
|
||||
import org.elasticsearch.watcher.support.clock.Clock;
|
||||
|
@ -16,7 +15,6 @@ import org.elasticsearch.watcher.transform.TransformRegistry;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -64,19 +62,4 @@ public class ActionRegistry {
|
|||
return new ExecutableActions(actions);
|
||||
}
|
||||
|
||||
public ExecutableActions.Results parseResults(Wid wid, XContentParser parser) throws IOException {
|
||||
Map<String, ActionWrapper.Result> results = new HashMap<>();
|
||||
|
||||
if (parser.currentToken() != XContentParser.Token.START_ARRAY) {
|
||||
throw new ActionException("could not parse action results for watch [{}]. expected an array of actions, but found [{}]", parser.currentToken());
|
||||
}
|
||||
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
ActionWrapper.Result result = ActionWrapper.Result.parse(wid, parser, this, transformRegistry);
|
||||
results.put(result.id(), result);
|
||||
}
|
||||
return new ExecutableActions.Results(results);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,17 +8,13 @@ package org.elasticsearch.watcher.actions;
|
|||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.watcher.actions.throttler.ActionThrottler;
|
||||
import org.elasticsearch.watcher.actions.throttler.Throttler;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.license.LicenseService;
|
||||
import org.elasticsearch.watcher.support.WatcherDateTimeUtils;
|
||||
import org.elasticsearch.watcher.support.clock.Clock;
|
||||
|
@ -29,8 +25,6 @@ import org.elasticsearch.watcher.watch.Payload;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -237,68 +231,6 @@ public class ActionWrapper implements ToXContent {
|
|||
builder.field(action.type(), action, params);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
static Result parse(Wid wid, XContentParser parser, ActionRegistry actionRegistry, TransformRegistry transformRegistry) throws IOException {
|
||||
assert parser.currentToken() == XContentParser.Token.START_OBJECT;
|
||||
|
||||
String id = null;
|
||||
Transform.Result transformResult = null;
|
||||
ActionFactory actionFactory = null;
|
||||
BytesReference actionResultSource = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Transform.Field.TRANSFORM.match(currentFieldName)) {
|
||||
transformResult = transformRegistry.parseResult(wid.watchId(), parser);
|
||||
} else if (Field.ID.match(currentFieldName)) {
|
||||
if (token == XContentParser.Token.VALUE_STRING) {
|
||||
id = parser.text();
|
||||
} else {
|
||||
throw new ActionException("could not parse action result for watch [{}]. expected a string value for [{}] but found [{}] instead", wid, currentFieldName, token);
|
||||
}
|
||||
} else {
|
||||
|
||||
// it's the type of the action
|
||||
|
||||
// here we don't directly parse the action type. instead we'll collect
|
||||
// the bytes of the structure that makes the action result. The reason
|
||||
// for this is that we want to make sure to pass the action id to the
|
||||
// action factory when we parse the result (so that error messages will
|
||||
// point to the action result that failed to parse). It's an overhead,
|
||||
// but for worth it for usability purposes.
|
||||
|
||||
actionFactory = actionRegistry.factory(currentFieldName);
|
||||
if (actionFactory == null) {
|
||||
throw new ActionException("could not parse action result for watch [{}]. unknown action type [{}]", wid, currentFieldName);
|
||||
}
|
||||
|
||||
// it would have been nice if we had access to the underlying byte offset
|
||||
// of the parser... but for now we'll just need to create a new json
|
||||
// builder with its own (new) byte array and copy over the content.
|
||||
XContentBuilder resultBuilder = jsonBuilder();
|
||||
XContentHelper.copyCurrentStructure(resultBuilder.generator(), parser);
|
||||
actionResultSource = resultBuilder.bytes();
|
||||
}
|
||||
}
|
||||
|
||||
if (id == null) {
|
||||
throw new ActionException("could not parse watch action result for watch [{}]. missing required [{}] field", wid, Field.ID.getPreferredName());
|
||||
}
|
||||
|
||||
if (actionFactory == null) {
|
||||
throw new ActionException("could not parse watch action result for watch [{}]. missing action result type", wid);
|
||||
}
|
||||
|
||||
assert actionResultSource != null : "if we parsed the type name we must have collected the type bytes";
|
||||
|
||||
parser = JsonXContent.jsonXContent.createParser(actionResultSource);
|
||||
parser.nextToken();
|
||||
Action.Result actionResult = actionFactory.parseResult(wid, id, parser);
|
||||
return new Result(id, transformResult, actionResult);
|
||||
}
|
||||
}
|
||||
|
||||
interface Field {
|
||||
|
|
|
@ -213,77 +213,6 @@ public class EmailAction implements Action {
|
|||
return builder.field(Field.EMAIL.getPreferredName(), email, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static Action.Result parse(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
Status status = null;
|
||||
Email email = null;
|
||||
String account = null;
|
||||
String reason = null;
|
||||
|
||||
XContentParser.Token token;
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.EMAIL.match(currentFieldName)) {
|
||||
try {
|
||||
email = Email.parse(parser);
|
||||
} catch (Email.ParseException pe) {
|
||||
throw new EmailActionException("could not parse [{}] action result [{}/{}]. failed parsing [{}] field", pe, TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if (Field.ACCOUNT.match(currentFieldName)) {
|
||||
account = parser.text();
|
||||
} else if (Field.REASON.match(currentFieldName)) {
|
||||
reason = parser.text();
|
||||
} else if (Field.STATUS.match(currentFieldName)) {
|
||||
try {
|
||||
status = Status.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new EmailActionException("could not parse [{}] action result [{}/{}]. unknown result status value [{}]", TYPE, watchId, actionId, parser.text());
|
||||
}
|
||||
} else {
|
||||
throw new EmailActionException("could not parse [{}] action result [{}/{}]. unexpected string field [{}]", TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new EmailActionException("could not parse [{}] action result [{}/{}]. unexpected token [{}]", TYPE, watchId, actionId, token);
|
||||
}
|
||||
}
|
||||
|
||||
if (status == null) {
|
||||
throw new EmailActionException("could not parse [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.STATUS.getPreferredName());
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
if (account == null) {
|
||||
throw new EmailActionException("could not parse [{}] action successful result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.ACCOUNT.getPreferredName());
|
||||
}
|
||||
if (email == null) {
|
||||
throw new EmailActionException("could not parse [{}] action successful result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.EMAIL.getPreferredName());
|
||||
}
|
||||
return new Success(account, email);
|
||||
|
||||
case SIMULATED:
|
||||
if (email == null) {
|
||||
throw new EmailActionException("could not parse [{}] action simulated result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.EMAIL.getPreferredName());
|
||||
}
|
||||
return new Simulated(email);
|
||||
|
||||
case THROTTLED:
|
||||
if (reason == null) {
|
||||
throw new EmailActionException("could not parse [{}] action throttled result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.REASON.getPreferredName());
|
||||
}
|
||||
return new Throttled(TYPE, reason);
|
||||
|
||||
default: // Failure
|
||||
assert status == Status.FAILURE : "unhandled action result status";
|
||||
if (reason == null) {
|
||||
throw new EmailActionException("could not parse [{}] action throttled result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.REASON.getPreferredName());
|
||||
}
|
||||
return new Failure(TYPE, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Action.Builder<EmailAction> {
|
||||
|
|
|
@ -46,11 +46,6 @@ public class EmailActionFactory extends ActionFactory<EmailAction, ExecutableEma
|
|||
return EmailAction.parse(watchId, actionId, parser, sanitizeHtmlBodyOfEmails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action.Result parseResult(Wid wid, String actionId, XContentParser parser) throws IOException {
|
||||
return EmailAction.Result.parse(wid.watchId(), actionId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableEmailAction createExecutable(EmailAction action) {
|
||||
return new ExecutableEmailAction(action, actionLogger, emailService, templateEngine);
|
||||
|
|
|
@ -9,13 +9,9 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Failure;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Status;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Throttled;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -104,88 +100,6 @@ public class IndexAction implements Action {
|
|||
return new IndexAction(index, docType);
|
||||
}
|
||||
|
||||
public static Action.Result parseResult(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
Status status = null;
|
||||
Payload response = null;
|
||||
String reason = null;
|
||||
Payload source = null;
|
||||
String index = null;
|
||||
String docType = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if (Field.REASON.match(currentFieldName)) {
|
||||
reason = parser.text();
|
||||
} else if (Field.STATUS.match(currentFieldName)) {
|
||||
try {
|
||||
status = Status.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new IndexActionException("could not parse [{}] action result [{}/{}]. unknown result status value [{}]", TYPE, watchId, actionId, parser.text());
|
||||
}
|
||||
} else {
|
||||
throw new IndexActionException("could not parse [{}] action result [{}/{}]. unexpected string field [{}]", TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Field.RESPONSE.match(currentFieldName)) {
|
||||
response = new Payload.Simple(parser.map());
|
||||
} else if (Field.REQUEST.match(currentFieldName)) {
|
||||
String context = currentFieldName;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if (Field.INDEX.match(currentFieldName)) {
|
||||
index = parser.text();
|
||||
} else if (Field.DOC_TYPE.match(currentFieldName)) {
|
||||
docType = parser.text();
|
||||
} else {
|
||||
throw new IndexActionException("could not parse [{}] action result [{}/{}]. unexpected string field [{}.{}]", TYPE, watchId, actionId, context, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Field.SOURCE.match(currentFieldName)) {
|
||||
source = new Payload.Simple(parser.map());
|
||||
} else {
|
||||
throw new IndexActionException("could not parse [{}] action result [{}/{}]. unexpected object field [{}.{}]", TYPE, watchId, actionId, context, currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IndexActionException("could not parse [{}] action result [{}/{}]. unexpected object field [{}]", TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new IndexActionException("could not parse [{}] action result [{}/{}]. unexpected token [{}]", TYPE, watchId, actionId, token);
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(status, "could not parse [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.STATUS.getPreferredName());
|
||||
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
assertNotNull(response, "could not parse executed [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.RESPONSE.getPreferredName());
|
||||
return new Result.Success(response);
|
||||
|
||||
case SIMULATED:
|
||||
assertNotNull(index, "could not parse simulated [{}] action result [{}/{}]. missing required [{}.{}] field", TYPE, watchId, actionId, Field.REQUEST.getPreferredName(), Field.INDEX.getPreferredName());
|
||||
assertNotNull(docType, "could not parse simulated [{}] action result [{}/{}]. missing required [{}.{}] field", TYPE, watchId, actionId, Field.REQUEST.getPreferredName(), Field.DOC_TYPE.getPreferredName());
|
||||
assertNotNull(source, "could not parse simulated [{}] action result [{}/{}]. missing required [{}.{}] field", TYPE, watchId, actionId, Field.REQUEST.getPreferredName(), Field.SOURCE.getPreferredName());
|
||||
return new Result.Simulated(index, docType, source);
|
||||
|
||||
case THROTTLED:
|
||||
assertNotNull(reason, "could not parse throttled [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.REASON.getPreferredName());
|
||||
return new Throttled(TYPE, reason);
|
||||
|
||||
default: // FAILURE
|
||||
assert status == Status.FAILURE : "unhandled action result status";
|
||||
assertNotNull(reason, "could not parse failure [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.REASON.getPreferredName());
|
||||
return new Failure(TYPE, reason);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void assertNotNull(Object value, String message, Object... args) {
|
||||
if (value == null) {
|
||||
throw new IndexActionException(message, args);
|
||||
|
|
|
@ -9,10 +9,8 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.ActionFactory;
|
||||
import org.elasticsearch.watcher.actions.email.ExecutableEmailAction;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -40,11 +38,6 @@ public class IndexActionFactory extends ActionFactory<IndexAction, ExecutableInd
|
|||
return IndexAction.parse(watchId, actionId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action.Result parseResult(Wid wid, String actionId, XContentParser parser) throws IOException {
|
||||
return IndexAction.parseResult(wid.watchId(), actionId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableIndexAction createExecutable(IndexAction action) {
|
||||
return new ExecutableIndexAction(action, actionLogger, client);
|
||||
|
|
|
@ -10,8 +10,6 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Failure;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Throttled;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -110,58 +108,6 @@ public class LoggingAction implements Action {
|
|||
return new LoggingAction(text, level, category);
|
||||
}
|
||||
|
||||
public static Action.Result parseResult(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
Action.Result.Status status = null;
|
||||
String loggedText = null;
|
||||
String reason = null;
|
||||
|
||||
XContentParser.Token token;
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if (Field.LOGGED_TEXT.match(currentFieldName)) {
|
||||
loggedText = parser.text();
|
||||
} else if (Field.REASON.match(currentFieldName)) {
|
||||
reason = parser.text();
|
||||
} else if (Field.STATUS.match(currentFieldName)) {
|
||||
try {
|
||||
status = Action.Result.Status.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new LoggingActionException("could not parse [{}] action result [{}/{}]. unknown result status value [{}]", TYPE, watchId, actionId, parser.text());
|
||||
}
|
||||
} else {
|
||||
throw new LoggingActionException("could not parse [{}] action result [{}/{}]. unexpected string field [{}]", TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new LoggingActionException("could not parse [{}] action result [{}/{}]. unexpected token [{}]", TYPE, watchId, actionId, token);
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(status, "could not parse [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.STATUS.getPreferredName());
|
||||
|
||||
switch (status) {
|
||||
|
||||
case SUCCESS:
|
||||
assertNotNull(loggedText, "could not parse successful [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.LOGGED_TEXT.getPreferredName());
|
||||
return new Result.Success(loggedText);
|
||||
|
||||
case SIMULATED:
|
||||
assertNotNull(loggedText, "could not parse simulated [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.LOGGED_TEXT.getPreferredName());
|
||||
return new Result.Simulated(loggedText);
|
||||
|
||||
case THROTTLED:
|
||||
assertNotNull(reason, "could not parse throttled [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.REASON.getPreferredName());
|
||||
return new Throttled(TYPE, reason);
|
||||
|
||||
default: // FAILURE
|
||||
assert status == Action.Result.Status.FAILURE : "unhandled action result status";
|
||||
assertNotNull(reason, "could not parse failure [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, Field.REASON.getPreferredName());
|
||||
return new Failure(TYPE, reason);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertNotNull(Object value, String message, Object... args) {
|
||||
if (value == null) {
|
||||
throw new LoggingActionException(message, args);
|
||||
|
|
|
@ -9,9 +9,7 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.ActionFactory;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -41,11 +39,6 @@ public class LoggingActionFactory extends ActionFactory<LoggingAction, Executabl
|
|||
return LoggingAction.parse(watchId, actionId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action.Result parseResult(Wid wid, String actionId, XContentParser parser) throws IOException {
|
||||
return LoggingAction.parseResult(wid.watchId(), actionId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableLoggingAction createExecutable(LoggingAction action) {
|
||||
return new ExecutableLoggingAction(action, actionLogger, settings, templateEngine);
|
||||
|
|
|
@ -9,15 +9,11 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Status;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Throttled;
|
||||
import org.elasticsearch.watcher.actions.logging.LoggingActionException;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
|
||||
import org.elasticsearch.watcher.support.http.HttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -70,75 +66,6 @@ public class WebhookAction implements Action {
|
|||
}
|
||||
}
|
||||
|
||||
public static Action.Result parseResult(String watchId, String actionId, XContentParser parser, HttpRequest.Parser requestParser) throws IOException {
|
||||
Status status = null;
|
||||
String reason = null;
|
||||
HttpRequest request = null;
|
||||
HttpResponse response = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.REQUEST.match(currentFieldName)) {
|
||||
try {
|
||||
request = requestParser.parse(parser);
|
||||
} catch (HttpRequest.Parser.ParseException pe) {
|
||||
throw new WebhookActionException("could not parse [{}] action result [{}/{}]. failed parsing [{}] field", pe, TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else if (Field.RESPONSE.match(currentFieldName)) {
|
||||
try {
|
||||
response = HttpResponse.parse(parser);
|
||||
} catch (HttpResponse.ParseException pe) {
|
||||
throw new WebhookActionException("could not parse [{}] action result [{}/{}]. failed parsing [{}] field", pe, TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if (Field.REASON.match(currentFieldName)) {
|
||||
reason = parser.text();
|
||||
} else if (Field.STATUS.match(currentFieldName)) {
|
||||
try {
|
||||
status = Status.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new LoggingActionException("could not parse [{}] action result [{}/{}]. unknown result status value [{}]", TYPE, watchId, actionId, parser.text());
|
||||
}
|
||||
} else {
|
||||
throw new WebhookActionException("could not parse [{}] action result [{}/{}]. unexpected string field [{}]", TYPE, watchId, actionId, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new WebhookActionException("could not parse [{}] action result [{}/{}]. unexpected token [{}]", TYPE, watchId, actionId, token);
|
||||
}
|
||||
}
|
||||
|
||||
assertNotNull(status, "could not parse [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.STATUS.getPreferredName());
|
||||
|
||||
switch (status) {
|
||||
|
||||
case SUCCESS:
|
||||
assertNotNull(request, "could not parse successful [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.REQUEST.getPreferredName());
|
||||
assertNotNull(response, "could not parse successful [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.RESPONSE.getPreferredName());
|
||||
return new Result.Success(request, response);
|
||||
|
||||
case SIMULATED:
|
||||
assertNotNull(request, "could not parse simulated [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.REQUEST.getPreferredName());
|
||||
return new Result.Simulated(request);
|
||||
|
||||
case THROTTLED:
|
||||
assertNotNull(reason, "could not parse throttled [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.REASON.getPreferredName());
|
||||
return new Throttled(TYPE, reason);
|
||||
|
||||
default: // Failure
|
||||
assert status == Status.FAILURE : "unhandled action result status";
|
||||
assertNotNull(reason, "could not parse failure [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.REASON.getPreferredName());
|
||||
if (request != null || response != null) {
|
||||
assertNotNull(request, "could not parse failure [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.REQUEST.getPreferredName());
|
||||
assertNotNull(response, "could not parse failure [{}] action result [{}/{}]. missing required [{}] field", TYPE, watchId, actionId, currentFieldName, Field.RESPONSE.getPreferredName());
|
||||
return new Result.Failure(request, response, reason);
|
||||
}
|
||||
return new Action.Result.Failure(TYPE, reason);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertNotNull(Object value, String message, Object... args) {
|
||||
if (value == null) {
|
||||
throw new WebhookActionException(message, args);
|
||||
|
|
|
@ -9,9 +9,7 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.ActionFactory;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
|
||||
|
@ -50,11 +48,6 @@ public class WebhookActionFactory extends ActionFactory<WebhookAction, Executabl
|
|||
return WebhookAction.parse(watchId, actionId, parser, requestTemplateParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action.Result parseResult(Wid wid, String actionId, XContentParser parser) throws IOException {
|
||||
return WebhookAction.parseResult(wid.watchId(), actionId, parser, requestParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableWebhookAction createExecutable(WebhookAction action) {
|
||||
return new ExecutableWebhookAction(action, actionLogger, httpClient, templateEngine);
|
||||
|
|
|
@ -31,11 +31,6 @@ public abstract class ConditionFactory<C extends Condition, R extends Condition.
|
|||
*/
|
||||
public abstract C parseCondition(String watchId, XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Parses the given xContent and creates a concrete result
|
||||
*/
|
||||
public abstract R parseResult(String watchId, XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates an {@link ExecutableCondition executable condition} for the given condition.
|
||||
*/
|
||||
|
|
|
@ -81,43 +81,6 @@ public class ConditionRegistry {
|
|||
return condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the xcontent and returns the appropriate condition result. Expecting the following format:
|
||||
* <code><pre>
|
||||
* {
|
||||
* "met" : true | false,
|
||||
* "condition_type" : {
|
||||
* ... // result body
|
||||
* }
|
||||
* }
|
||||
* </pre></code>
|
||||
*/
|
||||
public Condition.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
Condition.Result result = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (currentFieldName == null) {
|
||||
throw new ConditionException("could not parse condition result for watch [{}]. invalid definition. expected a field indicating the condition type, but found", watchId, token);
|
||||
} else if (Condition.Field.MET.match(currentFieldName)) {
|
||||
// we do nothing for now here.... the condition will still parse its state in the type itself
|
||||
} else {
|
||||
ConditionFactory factory = factories.get(currentFieldName);
|
||||
if (factory == null) {
|
||||
throw new ConditionException("could not parse condition result for watch [{}]. un known condition type [{}]", watchId, currentFieldName);
|
||||
}
|
||||
result = factory.parseResult(watchId, parser);
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
throw new ConditionException("could not parse condition result for watch [{}]. missing required condition type field", watchId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void writeResult(Condition.Result result, XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.startObject()
|
||||
.field(Condition.Field.MET.getPreferredName(), result.met())
|
||||
|
|
|
@ -52,17 +52,6 @@ public class AlwaysCondition implements Condition {
|
|||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject().endObject();
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
|
||||
throw new AlwaysConditionException("unable to parse [{}] condition result for watch [{}]. expected an empty object but found [{}]", TYPE, watchId, parser.currentName());
|
||||
}
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token != XContentParser.Token.END_OBJECT) {
|
||||
throw new AlwaysConditionException("unable to parse [{}] condition result for watch [{}]. expected an empty object but found [{}]", TYPE, watchId, parser.currentName());
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Condition.Builder<AlwaysCondition> {
|
||||
|
|
|
@ -36,11 +36,6 @@ public class AlwaysConditionFactory extends ConditionFactory<AlwaysCondition, Al
|
|||
return AlwaysCondition.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlwaysCondition.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return AlwaysCondition.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableAlwaysCondition createExecutable(AlwaysCondition condition) {
|
||||
return this.condition;
|
||||
|
|
|
@ -143,39 +143,6 @@ public class CompareCondition implements Condition {
|
|||
.field(Field.RESOLVED_VALUE.getPreferredName(), resolveValue)
|
||||
.endObject();
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
Object resolvedValue = null;
|
||||
boolean foundResolvedValue = false;
|
||||
Boolean met = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (currentFieldName == null) {
|
||||
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. expected a field but found [{}] instead", TYPE, watchId, token);
|
||||
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
|
||||
if (Field.MET.match(currentFieldName)) {
|
||||
met = parser.booleanValue();
|
||||
} else {
|
||||
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. unexpected boolean field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else if (Field.RESOLVED_VALUE.match(currentFieldName)) {
|
||||
resolvedValue = WatcherXContentUtils.readValue(parser, token);
|
||||
foundResolvedValue = true;
|
||||
} else {
|
||||
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. unexpected field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundResolvedValue) {
|
||||
throw new CompareConditionException("could not parse condition result [{}] for watcher [{}]. missing required field [{}]", TYPE, watchId, Field.RESOLVED_VALUE.getPreferredName());
|
||||
}
|
||||
|
||||
return new Result(resolvedValue, met);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Op {
|
||||
|
|
|
@ -37,11 +37,6 @@ public class CompareConditionFactory extends ConditionFactory<CompareCondition,
|
|||
return CompareCondition.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompareCondition.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return CompareCondition.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableCompareCondition createExecutable(CompareCondition condition) {
|
||||
return new ExecutableCompareCondition(condition, conditionLogger, clock);
|
||||
|
|
|
@ -52,17 +52,6 @@ public class NeverCondition implements Condition {
|
|||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject().endObject();
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
|
||||
throw new NeverConditionException("could not parse [{}] condition result for watch [{}]. expected an empty object but found [{}]", TYPE, watchId, parser.currentName());
|
||||
}
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token != XContentParser.Token.END_OBJECT) {
|
||||
throw new NeverConditionException("could not parse [{}] condition result for watch [{}]. expected an empty object but found [{}]", TYPE, watchId, parser.currentName());
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Condition.Builder<NeverCondition> {
|
||||
|
|
|
@ -36,11 +36,6 @@ public class NeverConditionFactory extends ConditionFactory<NeverCondition, Neve
|
|||
return NeverCondition.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NeverCondition.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return NeverCondition.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableNeverCondition createExecutable(NeverCondition condition) {
|
||||
return this.condition;
|
||||
|
|
|
@ -82,32 +82,6 @@ public class ScriptCondition implements Condition {
|
|||
.field(Field.MET.getPreferredName(), met)
|
||||
.endObject();
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
Boolean met = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
|
||||
if (Field.MET.match(currentFieldName)) {
|
||||
met = parser.booleanValue();
|
||||
} else {
|
||||
throw new ScriptConditionException("could not parse [{}] condition result for watch [{}]. unexpected boolean field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new ScriptConditionException("could not parse [{}] condition result for watch [{}]. unexpected token [{}]", TYPE, watchId, token);
|
||||
}
|
||||
}
|
||||
|
||||
if (met == null) {
|
||||
throw new ScriptConditionException("could not parse [{}] condition result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.MET.getPreferredName());
|
||||
}
|
||||
|
||||
return met ? ScriptCondition.Result.MET : ScriptCondition.Result.UNMET;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Condition.Builder<ScriptCondition> {
|
||||
|
|
|
@ -37,11 +37,6 @@ public class ScriptConditionFactory extends ConditionFactory<ScriptCondition, Sc
|
|||
return ScriptCondition.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptCondition.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return ScriptCondition.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableScriptCondition createExecutable(ScriptCondition condition) {
|
||||
return new ExecutableScriptCondition(condition, conditionLogger, scriptService);
|
||||
|
|
|
@ -26,6 +26,8 @@ public class ExecutionModule extends AbstractModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(TriggeredWatch.Parser.class).asEagerSingleton();
|
||||
bind(TriggeredWatchStore.class).asEagerSingleton();
|
||||
bind(ExecutionService.class).asEagerSingleton();
|
||||
bind(executorClass).asEagerSingleton();
|
||||
bind(triggerEngineListenerClass).asEagerSingleton();
|
||||
|
|
|
@ -16,12 +16,11 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
|
||||
import org.elasticsearch.index.engine.VersionConflictEngineException;
|
||||
import org.elasticsearch.index.engine.DocumentMissingException;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.history.*;
|
||||
import org.elasticsearch.watcher.input.Input;
|
||||
import org.elasticsearch.watcher.support.clock.Clock;
|
||||
import org.elasticsearch.watcher.support.validation.WatcherSettingsValidation;
|
||||
|
@ -42,6 +41,7 @@ import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
|||
public class ExecutionService extends AbstractComponent {
|
||||
|
||||
private final HistoryStore historyStore;
|
||||
private final TriggeredWatchStore triggeredWatchStore;
|
||||
private final WatchExecutor executor;
|
||||
private final WatchStore watchStore;
|
||||
private final WatchLockService watchLockService;
|
||||
|
@ -53,10 +53,11 @@ public class ExecutionService extends AbstractComponent {
|
|||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
@Inject
|
||||
public ExecutionService(Settings settings, HistoryStore historyStore, WatchExecutor executor, WatchStore watchStore,
|
||||
public ExecutionService(Settings settings, HistoryStore historyStore, TriggeredWatchStore triggeredWatchStore, WatchExecutor executor, WatchStore watchStore,
|
||||
WatchLockService watchLockService, Clock clock, WatcherSettingsValidation settingsValidation) {
|
||||
super(settings);
|
||||
this.historyStore = historyStore;
|
||||
this.triggeredWatchStore = triggeredWatchStore;
|
||||
this.executor = executor;
|
||||
this.watchStore = watchStore;
|
||||
this.watchLockService = watchLockService;
|
||||
|
@ -73,17 +74,18 @@ public class ExecutionService extends AbstractComponent {
|
|||
}
|
||||
|
||||
assert executor.queue().isEmpty() : "queue should be empty, but contains " + executor.queue().size() + " elements.";
|
||||
Collection<WatchRecord> records = historyStore.loadRecords(state, WatchRecord.State.AWAITS_EXECUTION);
|
||||
if (started.compareAndSet(false, true)) {
|
||||
logger.debug("starting execution service");
|
||||
historyStore.start();
|
||||
triggeredWatchStore.start();
|
||||
Collection<TriggeredWatch> records = triggeredWatchStore.loadTriggeredWatches(state);
|
||||
executeRecords(records);
|
||||
logger.debug("started execution service");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean validate(ClusterState state) {
|
||||
return historyStore.validate(state);
|
||||
return historyStore.validate(state) && triggeredWatchStore.validate(state);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
|
@ -93,6 +95,7 @@ public class ExecutionService extends AbstractComponent {
|
|||
// this is a forceful shutdown that also interrupts the worker threads in the threadpool
|
||||
List<Runnable> cancelledTasks = new ArrayList<>();
|
||||
executor.queue().drainTo(cancelledTasks);
|
||||
triggeredWatchStore.stop();
|
||||
historyStore.stop();
|
||||
logger.debug("cancelled [{}] queued tasks", cancelledTasks.size());
|
||||
logger.debug("stopped execution service");
|
||||
|
@ -134,7 +137,7 @@ public class ExecutionService extends AbstractComponent {
|
|||
if (!started.get()) {
|
||||
throw new ElasticsearchIllegalStateException("not started");
|
||||
}
|
||||
final LinkedList<WatchRecord> records = new LinkedList<>();
|
||||
final LinkedList<TriggeredWatch> triggeredWatches = new LinkedList<>();
|
||||
final LinkedList<TriggeredExecutionContext> contexts = new LinkedList<>();
|
||||
|
||||
DateTime now = clock.now(UTC);
|
||||
|
@ -146,39 +149,39 @@ public class ExecutionService extends AbstractComponent {
|
|||
}
|
||||
TriggeredExecutionContext ctx = new TriggeredExecutionContext(watch, now, event, defaultThrottlePeriod);
|
||||
contexts.add(ctx);
|
||||
records.add(new WatchRecord(ctx.id(), watch, event));
|
||||
triggeredWatches.add(new TriggeredWatch(ctx.id(), event));
|
||||
}
|
||||
|
||||
logger.debug("saving watch records [{}]", records.size());
|
||||
if (records.size() == 0) {
|
||||
logger.debug("saving watch records [{}]", triggeredWatches.size());
|
||||
if (triggeredWatches.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (records.size() == 1) {
|
||||
final WatchRecord watchRecord = records.getFirst();
|
||||
if (triggeredWatches.size() == 1) {
|
||||
final TriggeredWatch triggeredWatch = triggeredWatches.getFirst();
|
||||
final TriggeredExecutionContext ctx = contexts.getFirst();
|
||||
historyStore.put(watchRecord, new ActionListener<Boolean>() {
|
||||
triggeredWatchStore.put(triggeredWatch, new ActionListener<Boolean>() {
|
||||
@Override
|
||||
public void onResponse(Boolean aBoolean) {
|
||||
executeAsync(ctx, watchRecord);
|
||||
executeAsync(ctx, triggeredWatch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
Throwable cause = ExceptionsHelper.unwrapCause(e);
|
||||
if (cause instanceof EsRejectedExecutionException) {
|
||||
logger.debug("failed to store watch record [{}]/[{}] due to overloaded threadpool [{}]", watchRecord, ctx.id(), ExceptionsHelper.detailedMessage(e));
|
||||
logger.debug("failed to store watch record [{}]/[{}] due to overloaded threadpool [{}]", triggeredWatch, ctx.id(), ExceptionsHelper.detailedMessage(e));
|
||||
} else {
|
||||
logger.warn("failed to store watch record [{}]/[{}]", e, watchRecord, ctx.id());
|
||||
logger.warn("failed to store watch record [{}]/[{}]", e, triggeredWatch, ctx.id());
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
historyStore.putAll(records, new ActionListener<List<Integer>>() {
|
||||
triggeredWatchStore.putAll(triggeredWatches, new ActionListener<List<Integer>>() {
|
||||
@Override
|
||||
public void onResponse(List<Integer> successFullSlots) {
|
||||
for (Integer slot : successFullSlots) {
|
||||
executeAsync(contexts.get(slot), records.get(slot));
|
||||
executeAsync(contexts.get(slot), triggeredWatches.get(slot));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +202,7 @@ public class ExecutionService extends AbstractComponent {
|
|||
if (!started.get()) {
|
||||
throw new ElasticsearchIllegalStateException("not started");
|
||||
}
|
||||
final LinkedList<WatchRecord> records = new LinkedList<>();
|
||||
final LinkedList<TriggeredWatch> triggeredWatches = new LinkedList<>();
|
||||
final LinkedList<TriggeredExecutionContext> contexts = new LinkedList<>();
|
||||
|
||||
DateTime now = clock.now(UTC);
|
||||
|
@ -211,47 +214,46 @@ public class ExecutionService extends AbstractComponent {
|
|||
}
|
||||
TriggeredExecutionContext ctx = new TriggeredExecutionContext(watch, now, event, defaultThrottlePeriod);
|
||||
contexts.add(ctx);
|
||||
records.add(new WatchRecord(ctx.id(), watch, event));
|
||||
triggeredWatches.add(new TriggeredWatch(ctx.id(), event));
|
||||
}
|
||||
|
||||
logger.debug("saving watch records [{}]", records.size());
|
||||
if (records.size() == 0) {
|
||||
logger.debug("saving watch records [{}]", triggeredWatches.size());
|
||||
if (triggeredWatches.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (records.size() == 1) {
|
||||
final WatchRecord watchRecord = records.getFirst();
|
||||
if (triggeredWatches.size() == 1) {
|
||||
final TriggeredWatch triggeredWatch = triggeredWatches.getFirst();
|
||||
final TriggeredExecutionContext ctx = contexts.getFirst();
|
||||
historyStore.put(watchRecord);
|
||||
executeAsync(ctx, watchRecord);
|
||||
triggeredWatchStore.put(triggeredWatch);
|
||||
executeAsync(ctx, triggeredWatch);
|
||||
} else {
|
||||
List<Integer> slots = historyStore.putAll(records);
|
||||
List<Integer> slots = triggeredWatchStore.putAll(triggeredWatches);
|
||||
for (Integer slot : slots) {
|
||||
executeAsync(contexts.get(slot), records.get(slot));
|
||||
executeAsync(contexts.get(slot), triggeredWatches.get(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WatchRecord execute(WatchExecutionContext ctx) throws IOException {
|
||||
WatchRecord watchRecord = new WatchRecord(ctx.id(), ctx.watch(), ctx.triggerEvent());
|
||||
WatchRecord record;
|
||||
WatchLockService.Lock lock = watchLockService.acquire(ctx.watch().id());
|
||||
try {
|
||||
currentExecutions.put(ctx.watch().id(), new WatchExecution(ctx, Thread.currentThread()));
|
||||
WatchExecutionResult result = executeInner(ctx);
|
||||
watchRecord.seal(result);
|
||||
record = executeInner(ctx);
|
||||
if (ctx.recordExecution()) {
|
||||
watchStore.updateStatus(ctx.watch());
|
||||
}
|
||||
} catch (VersionConflictEngineException vcee) {
|
||||
throw new WatcherException("Failed to update the watch [{}] on execute perhaps it was force deleted", vcee, ctx.watch().id());
|
||||
} catch (DocumentMissingException vcee) {
|
||||
throw new WatchMissingException("failed to update the watch [{}] on execute perhaps it was force deleted", vcee, ctx.watch().id());
|
||||
} finally {
|
||||
currentExecutions.remove(ctx.watch().id());
|
||||
lock.release();
|
||||
}
|
||||
if (ctx.recordExecution()) {
|
||||
historyStore.put(watchRecord);
|
||||
historyStore.put(record);
|
||||
}
|
||||
return watchRecord;
|
||||
return record;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -264,17 +266,19 @@ public class ExecutionService extends AbstractComponent {
|
|||
triggered (it'll have its history record)
|
||||
*/
|
||||
|
||||
private void executeAsync(WatchExecutionContext ctx, WatchRecord watchRecord) {
|
||||
private void executeAsync(WatchExecutionContext ctx, TriggeredWatch triggeredWatch) {
|
||||
try {
|
||||
executor.execute(new WatchExecutionTask(ctx, watchRecord));
|
||||
executor.execute(new WatchExecutionTask(ctx));
|
||||
} catch (EsRejectedExecutionException e) {
|
||||
logger.debug("failed to execute triggered watch [{}]", watchRecord.watchId());
|
||||
watchRecord.update(WatchRecord.State.FAILED, "failed to run triggered watch [" + watchRecord.watchId() + "] due to thread pool capacity");
|
||||
historyStore.update(watchRecord);
|
||||
String message = "failed to run triggered watch [" + triggeredWatch.id() + "] due to thread pool capacity";
|
||||
logger.debug(message);
|
||||
WatchRecord record = ctx.abort(message, ExecutionState.FAILED);
|
||||
historyStore.put(record);
|
||||
triggeredWatchStore.delete(triggeredWatch.id());
|
||||
}
|
||||
}
|
||||
|
||||
WatchExecutionResult executeInner(WatchExecutionContext ctx) throws IOException {
|
||||
WatchRecord executeInner(WatchExecutionContext ctx) throws IOException {
|
||||
ctx.start();
|
||||
Watch watch = ctx.watch();
|
||||
ctx.beforeInput();
|
||||
|
@ -300,18 +304,19 @@ public class ExecutionService extends AbstractComponent {
|
|||
return ctx.finish();
|
||||
}
|
||||
|
||||
void executeRecords(Collection<WatchRecord> records) {
|
||||
assert records != null;
|
||||
void executeRecords(Collection<TriggeredWatch> triggeredWatches) {
|
||||
assert triggeredWatches != null;
|
||||
int counter = 0;
|
||||
for (WatchRecord record : records) {
|
||||
Watch watch = watchStore.get(record.watchId());
|
||||
for (TriggeredWatch triggeredWatch : triggeredWatches) {
|
||||
Watch watch = watchStore.get(triggeredWatch.id().watchId());
|
||||
if (watch == null) {
|
||||
String message = "unable to find watch for record [" + record.watchId() + "]/[" + record.id() + "], perhaps it has been deleted, ignoring...";
|
||||
record.update(WatchRecord.State.DELETED_WHILE_QUEUED, message);
|
||||
historyStore.update(record);
|
||||
String message = "unable to find watch for record [" + triggeredWatch.id().watchId() + "]/[" + triggeredWatch.id() + "], perhaps it has been deleted, ignoring...";
|
||||
WatchRecord record = new WatchRecord(triggeredWatch.id(), triggeredWatch.triggerEvent(), message, ExecutionState.DELETED_WHILE_QUEUED);
|
||||
historyStore.put(record);
|
||||
triggeredWatchStore.delete(triggeredWatch.id());
|
||||
} else {
|
||||
TriggeredExecutionContext ctx = new TriggeredExecutionContext(watch, clock.now(UTC), record.triggerEvent(), defaultThrottlePeriod);
|
||||
executeAsync(ctx, record);
|
||||
TriggeredExecutionContext ctx = new TriggeredExecutionContext(watch, clock.now(UTC), triggeredWatch.triggerEvent(), defaultThrottlePeriod);
|
||||
executeAsync(ctx, triggeredWatch);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
@ -320,12 +325,9 @@ public class ExecutionService extends AbstractComponent {
|
|||
|
||||
private final class WatchExecutionTask implements Runnable {
|
||||
|
||||
private final WatchRecord watchRecord;
|
||||
|
||||
private final WatchExecutionContext ctx;
|
||||
|
||||
private WatchExecutionTask(WatchExecutionContext ctx, WatchRecord watchRecord) {
|
||||
this.watchRecord = watchRecord;
|
||||
private WatchExecutionTask(WatchExecutionContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
|
@ -337,43 +339,41 @@ public class ExecutionService extends AbstractComponent {
|
|||
}
|
||||
logger.trace("executing [{}] [{}]", ctx.watch().id(), ctx.id());
|
||||
|
||||
WatchRecord record = null;
|
||||
WatchLockService.Lock lock = watchLockService.acquire(ctx.watch().id());
|
||||
try {
|
||||
currentExecutions.put(ctx.watch().id(), new WatchExecution(ctx, Thread.currentThread()));
|
||||
if (watchStore.get(ctx.watch().id()) == null) {
|
||||
//Fail fast if we are trying to execute a deleted watch
|
||||
String message = "unable to find watch for record [" + watchRecord.id() + "], perhaps it has been deleted, ignoring...";
|
||||
watchRecord.update(WatchRecord.State.DELETED_WHILE_QUEUED, message);
|
||||
String message = "unable to find watch for record [" + ctx.id() + "], perhaps it has been deleted, ignoring...";
|
||||
record = ctx.abort(message, ExecutionState.DELETED_WHILE_QUEUED);
|
||||
} else {
|
||||
watchRecord.update(WatchRecord.State.CHECKING, null);
|
||||
logger.debug("checking watch [{}]", watchRecord.watchId());
|
||||
WatchExecutionResult result = executeInner(ctx);
|
||||
watchRecord.seal(result);
|
||||
logger.debug("checking watch [{}]", ctx.id().watchId());
|
||||
record = executeInner(ctx);
|
||||
if (ctx.recordExecution()) {
|
||||
watchStore.updateStatus(ctx.watch());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (started()) {
|
||||
String detailedMessage = ExceptionsHelper.detailedMessage(e);
|
||||
logger.warn("failed to execute watch [{}]/[{}], failure [{}]", watchRecord.watchId(), ctx.id(), detailedMessage);
|
||||
watchRecord.update(WatchRecord.State.FAILED, detailedMessage);
|
||||
} else {
|
||||
logger.debug("failed to execute watch [{}] after shutdown", e, watchRecord);
|
||||
}
|
||||
logger.warn("failed to execute watch [{}], failure [{}]", ctx.id().value(), detailedMessage);
|
||||
record = ctx.abort(detailedMessage, ExecutionState.FAILED);
|
||||
} finally {
|
||||
// The recordExecution doesn't need to check if it is in a started state here, because the
|
||||
// ExecutionService doesn't stop before the WatchLockService stops
|
||||
if (record != null && ctx.recordExecution()) {
|
||||
try {
|
||||
historyStore.put(record);
|
||||
triggeredWatchStore.delete(ctx.id());
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to update watch record [{}], failure [{}], record failure if any [{}]", ctx.id(), ExceptionsHelper.detailedMessage(e), record.message());
|
||||
}
|
||||
}
|
||||
currentExecutions.remove(ctx.watch().id());
|
||||
lock.release();
|
||||
logger.trace("finished [{}]/[{}]", ctx.watch().id(), ctx.id());
|
||||
}
|
||||
|
||||
if (ctx.recordExecution() && started()) {
|
||||
try {
|
||||
historyStore.update(watchRecord);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to update watch record [{}]/[{}], failure [{}], record failure if any [{}]", watchRecord.watchId(), ctx.id(), ExceptionsHelper.detailedMessage(e), watchRecord.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.watcher.execution;
|
||||
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum ExecutionState {
|
||||
|
||||
EXECUTION_NOT_NEEDED,
|
||||
THROTTLED,
|
||||
EXECUTED,
|
||||
FAILED,
|
||||
DELETED_WHILE_QUEUED;
|
||||
|
||||
public String id() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public static ExecutionState resolve(String id) {
|
||||
try {
|
||||
return valueOf(id.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new WatcherException("unknown execution state [{}]", id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.watcher.execution;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.watcher.trigger.TriggerService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TriggeredWatch implements ToXContent {
|
||||
|
||||
private final Wid id;
|
||||
private final TriggerEvent triggerEvent;
|
||||
|
||||
public TriggeredWatch(Wid id, TriggerEvent triggerEvent) {
|
||||
this.id = id;
|
||||
this.triggerEvent = triggerEvent;
|
||||
}
|
||||
|
||||
public Wid id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public TriggerEvent triggerEvent() {
|
||||
return triggerEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.startObject(Field.TRIGGER_EVENT.getPreferredName())
|
||||
.field(triggerEvent.type(), triggerEvent, params)
|
||||
.endObject();
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static class Parser extends AbstractComponent {
|
||||
|
||||
private final TriggerService triggerService;
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings, TriggerService triggerService) {
|
||||
super(settings);
|
||||
this.triggerService = triggerService;
|
||||
}
|
||||
|
||||
public TriggeredWatch parse(String id, long version, BytesReference source) {
|
||||
try (XContentParser parser = XContentHelper.createParser(source)) {
|
||||
return parse(id, version, parser);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("unable to parse watch record", e);
|
||||
}
|
||||
}
|
||||
|
||||
public TriggeredWatch parse(String id, long version, XContentParser parser) throws IOException {
|
||||
assert id != null : "watch record id is missing";
|
||||
|
||||
Wid wid = new Wid(id);
|
||||
TriggerEvent triggerEvent = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.START_OBJECT;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Field.TRIGGER_EVENT.match(currentFieldName)) {
|
||||
triggerEvent = triggerService.parseTriggerEvent(wid.watchId(), id, parser);
|
||||
} else {
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TriggeredWatch record = new TriggeredWatch(wid, triggerEvent);
|
||||
assert record.triggerEvent() != null : "watch record [" + id +"] is missing trigger";
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TriggeredWatch entry = (TriggeredWatch) o;
|
||||
if (!id.equals(entry.id)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
public interface Field {
|
||||
ParseField TRIGGER_EVENT = new ParseField("trigger_event");
|
||||
ParseField STATE = new ParseField("state");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* 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.watcher.execution;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.watcher.history.HistoryException;
|
||||
import org.elasticsearch.watcher.history.TriggeredWatchException;
|
||||
import org.elasticsearch.watcher.support.TemplateUtils;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
public class TriggeredWatchStore extends AbstractComponent {
|
||||
|
||||
public static final String INDEX_NAME = ".triggered_watches";
|
||||
public static final String DOC_TYPE = "triggered_watch";
|
||||
public static final String INDEX_TEMPLATE_NAME = "triggered_watches";
|
||||
private static final ImmutableSet<String> forbiddenIndexSettings = ImmutableSet.of("index.mapper.dynamic");
|
||||
|
||||
private final int scrollSize;
|
||||
private final ClientProxy client;
|
||||
private final TimeValue scrollTimeout;
|
||||
private final TemplateUtils templateUtils;
|
||||
private final Settings customIndexSettings;
|
||||
private final TriggeredWatch.Parser triggeredWatchParser;
|
||||
|
||||
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
private final Lock accessLock = readWriteLock.readLock();
|
||||
private final Lock stopLock = readWriteLock.writeLock();
|
||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||
|
||||
@Inject
|
||||
public TriggeredWatchStore(Settings settings, ClientProxy client, TemplateUtils templateUtils, TriggeredWatch.Parser triggeredWatchParser) {
|
||||
super(settings);
|
||||
this.scrollSize = componentSettings.getAsInt("scroll.size", 100);
|
||||
this.client = client;
|
||||
this.scrollTimeout = componentSettings.getAsTime("scroll.timeout", TimeValue.timeValueSeconds(30));
|
||||
this.templateUtils = templateUtils;
|
||||
this.customIndexSettings = updateTriggerWatchesSettings(settings);
|
||||
this.triggeredWatchParser = triggeredWatchParser;
|
||||
}
|
||||
|
||||
private Settings updateTriggerWatchesSettings(Settings nodeSettings) {
|
||||
Settings newSettings = ImmutableSettings.builder()
|
||||
.put(nodeSettings.getAsSettings("watcher.triggered_watches.index"))
|
||||
.build();
|
||||
if (newSettings.names().isEmpty()) {
|
||||
return ImmutableSettings.EMPTY;
|
||||
}
|
||||
|
||||
// Filter out forbidden settings:
|
||||
ImmutableSettings.Builder builder = ImmutableSettings.builder();
|
||||
for (Map.Entry<String, String> entry : newSettings.getAsMap().entrySet()) {
|
||||
String name = "index." + entry.getKey();
|
||||
if (forbiddenIndexSettings.contains(name)) {
|
||||
logger.warn("overriding the default [{}} setting is forbidden. ignoring...", name);
|
||||
continue;
|
||||
}
|
||||
builder.put(name, entry.getValue());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
public void start() {
|
||||
if (started.compareAndSet(false, true)) {
|
||||
templateUtils.putTemplate(INDEX_TEMPLATE_NAME, customIndexSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean validate(ClusterState state) {
|
||||
IndexMetaData indexMetaData = state.getMetaData().index(INDEX_NAME);
|
||||
if (indexMetaData != null) {
|
||||
if (!state.routingTable().index(INDEX_NAME).allPrimaryShardsActive()) {
|
||||
logger.debug("not all primary shards of the [{}] index are started, so we cannot load previous triggered watches", INDEX_NAME);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
logger.debug("triggered watch index doesn't exist, so we can load");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopLock.lock(); // This will block while put or update actions are underway
|
||||
try {
|
||||
started.set(false);
|
||||
} finally {
|
||||
stopLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void put(TriggeredWatch triggeredWatch) throws HistoryException {
|
||||
ensureStarted();
|
||||
accessLock.lock();
|
||||
try {
|
||||
IndexRequest request = new IndexRequest(INDEX_NAME, DOC_TYPE, triggeredWatch.id().value())
|
||||
.source(XContentFactory.jsonBuilder().value(triggeredWatch))
|
||||
.opType(IndexRequest.OpType.CREATE);
|
||||
client.index(request);
|
||||
} catch (IOException e) {
|
||||
throw new TriggeredWatchException("failed to persist triggered watch [{}]", e, triggeredWatch);
|
||||
} finally {
|
||||
accessLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void put(final TriggeredWatch triggeredWatch, final ActionListener<Boolean> listener) throws TriggeredWatchException {
|
||||
ensureStarted();
|
||||
try {
|
||||
IndexRequest request = new IndexRequest(INDEX_NAME, DOC_TYPE, triggeredWatch.id().value())
|
||||
.source(XContentFactory.jsonBuilder().value(triggeredWatch))
|
||||
.opType(IndexRequest.OpType.CREATE);
|
||||
client.index(request, new ActionListener<IndexResponse>() {
|
||||
@Override
|
||||
public void onResponse(IndexResponse response) {
|
||||
listener.onResponse(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new TriggeredWatchException("failed to persist triggered watch [{}]", e, triggeredWatch);
|
||||
}
|
||||
}
|
||||
|
||||
public void putAll(final List<TriggeredWatch> triggeredWatches, final ActionListener<List<Integer>> listener) throws TriggeredWatchException {
|
||||
ensureStarted();
|
||||
try {
|
||||
BulkRequest request = new BulkRequest();
|
||||
for (TriggeredWatch triggeredWatch : triggeredWatches) {
|
||||
IndexRequest indexRequest = new IndexRequest(INDEX_NAME, DOC_TYPE, triggeredWatch.id().value());
|
||||
indexRequest.source(XContentFactory.jsonBuilder().value(triggeredWatch));
|
||||
indexRequest.opType(IndexRequest.OpType.CREATE);
|
||||
request.add(indexRequest);
|
||||
}
|
||||
client.bulk(request, new ActionListener<BulkResponse>() {
|
||||
@Override
|
||||
public void onResponse(BulkResponse response) {
|
||||
List<Integer> successFullSlots = new ArrayList<Integer>();
|
||||
for (int i = 0; i < response.getItems().length; i++) {
|
||||
BulkItemResponse itemResponse = response.getItems()[i];
|
||||
if (itemResponse.isFailed()) {
|
||||
logger.error("could store triggered watch with id [{}], because failed [{}]", itemResponse.getId(), itemResponse.getFailureMessage());
|
||||
} else {
|
||||
IndexResponse indexResponse = itemResponse.getResponse();
|
||||
successFullSlots.add(i);
|
||||
}
|
||||
}
|
||||
listener.onResponse(successFullSlots);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new TriggeredWatchException("failed to persist triggered watches", e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Integer> putAll(final List<TriggeredWatch> triggeredWatches) throws TriggeredWatchException {
|
||||
ensureStarted();
|
||||
try {
|
||||
BulkRequest request = new BulkRequest();
|
||||
for (TriggeredWatch triggeredWatch : triggeredWatches) {
|
||||
IndexRequest indexRequest = new IndexRequest(INDEX_NAME, DOC_TYPE, triggeredWatch.id().value());
|
||||
indexRequest.source(XContentFactory.jsonBuilder().value(triggeredWatch));
|
||||
indexRequest.opType(IndexRequest.OpType.CREATE);
|
||||
request.add(indexRequest);
|
||||
}
|
||||
BulkResponse response = client.bulk(request);
|
||||
List<Integer> successFullSlots = new ArrayList<>();
|
||||
for (int i = 0; i < response.getItems().length; i++) {
|
||||
BulkItemResponse itemResponse = response.getItems()[i];
|
||||
if (itemResponse.isFailed()) {
|
||||
logger.error("could store triggered watch with id [{}], because failed [{}]", itemResponse.getId(), itemResponse.getFailureMessage());
|
||||
} else {
|
||||
IndexResponse indexResponse = itemResponse.getResponse();
|
||||
successFullSlots.add(i);
|
||||
}
|
||||
}
|
||||
return successFullSlots;
|
||||
} catch (IOException e) {
|
||||
throw new TriggeredWatchException("failed to persist triggered watches", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(Wid wid) throws TriggeredWatchException {
|
||||
ensureStarted();
|
||||
accessLock.lock();
|
||||
try {
|
||||
DeleteRequest request = new DeleteRequest(INDEX_NAME, DOC_TYPE, wid.value());
|
||||
client.delete(request);
|
||||
logger.trace("successfully deleted triggered watch with id [{}]", wid);
|
||||
} finally {
|
||||
accessLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<TriggeredWatch> loadTriggeredWatches(ClusterState state) {
|
||||
IndexMetaData indexMetaData = state.getMetaData().index(INDEX_NAME);
|
||||
if (indexMetaData == null) {
|
||||
logger.debug("no .watch_history indices found. skipping loading awaiting triggered watches");
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
int numPrimaryShards;
|
||||
if (!state.routingTable().index(INDEX_NAME).allPrimaryShardsActive()) {
|
||||
throw new TriggeredWatchException("not all primary shards of the [{}] index are started.", INDEX_NAME);
|
||||
} else {
|
||||
numPrimaryShards = indexMetaData.numberOfShards();
|
||||
}
|
||||
RefreshResponse refreshResponse = client.refresh(new RefreshRequest(INDEX_NAME));
|
||||
if (refreshResponse.getSuccessfulShards() < numPrimaryShards) {
|
||||
throw new TriggeredWatchException("refresh was supposed to run on [{}] shards, but ran on [{}] shards", numPrimaryShards, refreshResponse.getSuccessfulShards());
|
||||
}
|
||||
|
||||
SearchRequest searchRequest = createScanSearchRequest();
|
||||
SearchResponse response = client.search(searchRequest);
|
||||
List<TriggeredWatch> triggeredWatches = new ArrayList<>();
|
||||
try {
|
||||
if (response.getTotalShards() != response.getSuccessfulShards()) {
|
||||
throw new TriggeredWatchException("scan search was supposed to run on [{}] shards, but ran on [{}] shards", numPrimaryShards, response.getSuccessfulShards());
|
||||
}
|
||||
|
||||
if (response.getHits().getTotalHits() > 0) {
|
||||
response = client.searchScroll(response.getScrollId(), scrollTimeout);
|
||||
while (response.getHits().hits().length != 0) {
|
||||
for (SearchHit sh : response.getHits()) {
|
||||
String id = sh.getId();
|
||||
try {
|
||||
TriggeredWatch triggeredWatch = triggeredWatchParser.parse(id, sh.version(), sh.getSourceRef());
|
||||
logger.debug("loaded triggered watch [{}/{}/{}]", sh.index(), sh.type(), sh.id());
|
||||
triggeredWatches.add(triggeredWatch);
|
||||
} catch (Exception e) {
|
||||
logger.error("couldn't load triggered watch [{}], ignoring it...", e, id);
|
||||
}
|
||||
}
|
||||
response = client.searchScroll(response.getScrollId(), scrollTimeout);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
client.clearScroll(response.getScrollId());
|
||||
}
|
||||
return triggeredWatches;
|
||||
}
|
||||
|
||||
private SearchRequest createScanSearchRequest() {
|
||||
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
|
||||
.size(scrollSize);
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
|
||||
searchRequest.source(sourceBuilder);
|
||||
searchRequest.searchType(SearchType.SCAN);
|
||||
searchRequest.types(DOC_TYPE);
|
||||
searchRequest.scroll(scrollTimeout);
|
||||
searchRequest.preference("_primary");
|
||||
return searchRequest;
|
||||
}
|
||||
|
||||
private void ensureStarted() {
|
||||
if (!started.get()) {
|
||||
throw new TriggeredWatchException("unable to persist triggered watches, the store is not ready");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,11 +6,12 @@
|
|||
package org.elasticsearch.watcher.execution;
|
||||
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||
import org.elasticsearch.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.watcher.actions.ExecutableActions;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.input.Input;
|
||||
import org.elasticsearch.watcher.transform.ExecutableTransform;
|
||||
import org.elasticsearch.watcher.transform.Transform;
|
||||
|
@ -18,8 +19,8 @@ import org.elasticsearch.watcher.trigger.TriggerEvent;
|
|||
import org.elasticsearch.watcher.watch.Payload;
|
||||
import org.elasticsearch.watcher.watch.Watch;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -177,10 +178,15 @@ public abstract class WatchExecutionContext {
|
|||
actualExecutionStartMs = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public WatchExecutionResult finish() {
|
||||
public WatchRecord finish() {
|
||||
executionPhase = ExecutionPhase.FINISHED;
|
||||
long executionFinishMs = System.currentTimeMillis();
|
||||
return new WatchExecutionResult(this, executionFinishMs - actualExecutionStartMs);
|
||||
WatchExecutionResult result = new WatchExecutionResult(this, executionFinishMs - actualExecutionStartMs);
|
||||
return new WatchRecord(this, result);
|
||||
}
|
||||
|
||||
public WatchRecord abort(String message, ExecutionState state) {
|
||||
return new WatchRecord(id, triggerEvent, message, state);
|
||||
}
|
||||
|
||||
public WatchExecutionSnapshot createSnapshot(Thread executionThread) {
|
||||
|
|
|
@ -10,22 +10,15 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.actions.ActionRegistry;
|
||||
import org.elasticsearch.watcher.actions.ExecutableActions;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.condition.ConditionRegistry;
|
||||
import org.elasticsearch.watcher.input.Input;
|
||||
import org.elasticsearch.watcher.input.InputRegistry;
|
||||
import org.elasticsearch.watcher.support.WatcherDateTimeUtils;
|
||||
import org.elasticsearch.watcher.transform.Transform;
|
||||
import org.elasticsearch.watcher.transform.TransformRegistry;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -101,52 +94,6 @@ public class WatchExecutionResult implements ToXContent {
|
|||
return builder;
|
||||
}
|
||||
|
||||
public static class Parser {
|
||||
|
||||
public static WatchExecutionResult parse(Wid wid, XContentParser parser, ConditionRegistry conditionRegistry, ActionRegistry actionRegistry,
|
||||
InputRegistry inputRegistry, TransformRegistry transformRegistry) throws IOException {
|
||||
DateTime executionTime = null;
|
||||
long executionDurationMs = 0;
|
||||
boolean throttled = false;
|
||||
String throttleReason = null;
|
||||
ExecutableActions.Results actionResults = null;
|
||||
Input.Result inputResult = null;
|
||||
Condition.Result conditionResult = null;
|
||||
Transform.Result transformResult = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.EXECUTION_TIME.match(currentFieldName)) {
|
||||
try {
|
||||
executionTime = WatcherDateTimeUtils.parseDate(currentFieldName, parser, UTC);
|
||||
} catch (WatcherDateTimeUtils.ParseException pe) {
|
||||
throw new WatcherException("could not parse watch execution [{}]. failed to parse date field [{}]", pe, wid, currentFieldName);
|
||||
}
|
||||
} else if (Field.ACTIONS.match(currentFieldName)) {
|
||||
actionResults = actionRegistry.parseResults(wid, parser);
|
||||
} else if (Field.INPUT.match(currentFieldName)) {
|
||||
inputResult = inputRegistry.parseResult(wid.watchId(), parser);
|
||||
} else if (Field.CONDITION.match(currentFieldName)) {
|
||||
conditionResult = conditionRegistry.parseResult(wid.watchId(), parser);
|
||||
} else if (Transform.Field.TRANSFORM.match(currentFieldName)) {
|
||||
transformResult = transformRegistry.parseResult(wid.watchId(), parser);
|
||||
} else if (Field.EXECUTION_DURATION.match(currentFieldName) && token.isValue()) {
|
||||
executionDurationMs = parser.longValue();
|
||||
} else {
|
||||
throw new WatcherException("could not parse watch execution [{}]. unexpected field [{}]", wid, currentFieldName);
|
||||
}
|
||||
}
|
||||
if (executionTime == null) {
|
||||
throw new WatcherException("could not parse watch execution [{}]. missing required date field [{}]", wid, Field.EXECUTION_TIME.getPreferredName());
|
||||
}
|
||||
|
||||
return new WatchExecutionResult(executionTime, executionDurationMs, inputResult, conditionResult, transformResult, actionResults);
|
||||
}
|
||||
}
|
||||
|
||||
interface Field {
|
||||
ParseField EXECUTION_TIME = new ParseField("execution_time");
|
||||
ParseField INPUT = new ParseField("input");
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.watcher.execution;
|
||||
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
|
||||
public class WatchMissingException extends WatcherException {
|
||||
|
||||
public WatchMissingException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
public WatchMissingException(String msg, Throwable cause, Object... args) {
|
||||
super(msg, cause, args);
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ public class HistoryModule extends AbstractModule {
|
|||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(WatchRecord.Parser.class).asEagerSingleton();
|
||||
bind(HistoryStore.class).asEagerSingleton();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,23 +6,12 @@
|
|||
package org.elasticsearch.watcher.history;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.settings.ClusterDynamicSettings;
|
||||
import org.elasticsearch.cluster.settings.DynamicSettings;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
|
@ -30,18 +19,14 @@ import org.elasticsearch.common.joda.time.format.DateTimeFormat;
|
|||
import org.elasticsearch.common.joda.time.format.DateTimeFormatter;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.support.TemplateUtils;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
@ -60,10 +45,8 @@ public class HistoryStore extends AbstractComponent implements NodeSettingsServi
|
|||
|
||||
private final ClientProxy client;
|
||||
private final TemplateUtils templateUtils;
|
||||
private final int scrollSize;
|
||||
private final TimeValue scrollTimeout;
|
||||
private final WatchRecord.Parser recordParser;
|
||||
private final ThreadPool threadPool;
|
||||
|
||||
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
private final Lock putUpdateLock = readWriteLock.readLock();
|
||||
private final Lock stopLock = readWriteLock.writeLock();
|
||||
|
@ -72,16 +55,12 @@ public class HistoryStore extends AbstractComponent implements NodeSettingsServi
|
|||
private volatile Settings customIndexSettings = ImmutableSettings.EMPTY;
|
||||
|
||||
@Inject
|
||||
public HistoryStore(Settings settings, ClientProxy client, TemplateUtils templateUtils, WatchRecord.Parser recordParser,
|
||||
NodeSettingsService nodeSettingsService, @ClusterDynamicSettings DynamicSettings dynamicSettings,
|
||||
ThreadPool threadPool) {
|
||||
public HistoryStore(Settings settings, ClientProxy client, TemplateUtils templateUtils, NodeSettingsService nodeSettingsService,
|
||||
@ClusterDynamicSettings DynamicSettings dynamicSettings, ThreadPool threadPool) {
|
||||
super(settings);
|
||||
this.client = client;
|
||||
this.templateUtils = templateUtils;
|
||||
this.recordParser = recordParser;
|
||||
this.threadPool = threadPool;
|
||||
this.scrollTimeout = componentSettings.getAsTime("scroll.timeout", TimeValue.timeValueSeconds(30));
|
||||
this.scrollSize = componentSettings.getAsInt("scroll.size", 100);
|
||||
|
||||
updateHistorySettings(settings, false);
|
||||
nodeSettingsService.addListener(this);
|
||||
|
@ -138,7 +117,9 @@ public class HistoryStore extends AbstractComponent implements NodeSettingsServi
|
|||
|
||||
|
||||
public void start() {
|
||||
started.set(true);
|
||||
if (started.compareAndSet(false, true)) {
|
||||
templateUtils.putTemplate(INDEX_TEMPLATE_NAME, customIndexSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean validate(ClusterState state) {
|
||||
|
@ -181,8 +162,7 @@ public class HistoryStore extends AbstractComponent implements NodeSettingsServi
|
|||
IndexRequest request = new IndexRequest(index, DOC_TYPE, watchRecord.id().value())
|
||||
.source(XContentFactory.jsonBuilder().value(watchRecord))
|
||||
.opType(IndexRequest.OpType.CREATE);
|
||||
IndexResponse response = client.index(request);
|
||||
watchRecord.version(response.getVersion());
|
||||
client.index(request);
|
||||
} catch (IOException e) {
|
||||
throw new HistoryException("failed to persist watch record [" + watchRecord + "]", e);
|
||||
} finally {
|
||||
|
@ -190,180 +170,6 @@ public class HistoryStore extends AbstractComponent implements NodeSettingsServi
|
|||
}
|
||||
}
|
||||
|
||||
public void put(final WatchRecord watchRecord, final ActionListener<Boolean> listener) throws HistoryException {
|
||||
String index = getHistoryIndexNameForTime(watchRecord.triggerEvent().triggeredTime());
|
||||
try {
|
||||
IndexRequest request = new IndexRequest(index, DOC_TYPE, watchRecord.id().value())
|
||||
.source(XContentFactory.jsonBuilder().value(watchRecord))
|
||||
.opType(IndexRequest.OpType.CREATE);
|
||||
client.index(request, new ActionListener<IndexResponse>() {
|
||||
@Override
|
||||
public void onResponse(IndexResponse response) {
|
||||
watchRecord.version(response.getVersion());
|
||||
listener.onResponse(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new HistoryException("failed to persist watch record [" + watchRecord + "]", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void putAll(final List<WatchRecord> records, final ActionListener<List<Integer>> listener) throws HistoryException {
|
||||
try {
|
||||
BulkRequest request = new BulkRequest();
|
||||
for (WatchRecord record : records) {
|
||||
String index = getHistoryIndexNameForTime(record.triggerEvent().triggeredTime());
|
||||
IndexRequest indexRequest = new IndexRequest(index, DOC_TYPE, record.id().value());
|
||||
indexRequest.source(XContentFactory.jsonBuilder().value(record));
|
||||
indexRequest.opType(IndexRequest.OpType.CREATE);
|
||||
request.add(indexRequest);
|
||||
}
|
||||
client.bulk(request, new ActionListener<BulkResponse>() {
|
||||
@Override
|
||||
public void onResponse(BulkResponse response) {
|
||||
List<Integer> successFullSlots = new ArrayList<Integer>();
|
||||
for (int i = 0; i < response.getItems().length; i++) {
|
||||
BulkItemResponse itemResponse = response.getItems()[i];
|
||||
if (itemResponse.isFailed()) {
|
||||
logger.error("could store watch record with id [" + itemResponse.getId() + "], because failed [" + itemResponse.getFailureMessage() + "]");
|
||||
} else {
|
||||
IndexResponse indexResponse = itemResponse.getResponse();
|
||||
records.get(i).version(indexResponse.getVersion());
|
||||
successFullSlots.add(i);
|
||||
}
|
||||
}
|
||||
listener.onResponse(successFullSlots);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
listener.onFailure(e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new HistoryException("failed to persist watch records", e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Integer> putAll(final List<WatchRecord> records) throws HistoryException {
|
||||
try {
|
||||
BulkRequest request = new BulkRequest();
|
||||
for (WatchRecord record : records) {
|
||||
String index = getHistoryIndexNameForTime(record.triggerEvent().triggeredTime());
|
||||
IndexRequest indexRequest = new IndexRequest(index, DOC_TYPE, record.id().value());
|
||||
indexRequest.source(XContentFactory.jsonBuilder().value(record));
|
||||
indexRequest.opType(IndexRequest.OpType.CREATE);
|
||||
request.add(indexRequest);
|
||||
}
|
||||
BulkResponse response = client.bulk(request);
|
||||
List<Integer> successFullSlots = new ArrayList<>();
|
||||
for (int i = 0; i < response.getItems().length; i++) {
|
||||
BulkItemResponse itemResponse = response.getItems()[i];
|
||||
if (itemResponse.isFailed()) {
|
||||
logger.error("could store watch record with id [" + itemResponse.getId() + "], because failed [" + itemResponse.getFailureMessage() + "]");
|
||||
} else {
|
||||
IndexResponse indexResponse = itemResponse.getResponse();
|
||||
records.get(i).version(indexResponse.getVersion());
|
||||
successFullSlots.add(i);
|
||||
}
|
||||
}
|
||||
return successFullSlots;
|
||||
} catch (IOException e) {
|
||||
throw new HistoryException("failed to persist watch records", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(WatchRecord watchRecord) throws HistoryException {
|
||||
if (!started.get()) {
|
||||
throw new HistoryException("unable to persist watch record history store is not ready");
|
||||
}
|
||||
putUpdateLock.lock();
|
||||
try {
|
||||
BytesReference source = XContentFactory.jsonBuilder().value(watchRecord).bytes();
|
||||
IndexRequest request = new IndexRequest(getHistoryIndexNameForTime(watchRecord.triggerEvent().triggeredTime()), DOC_TYPE, watchRecord.id().value())
|
||||
.version(watchRecord.version());
|
||||
// TODO (2.0 upgrade): move back to BytesReference instead of dealing with the array directly
|
||||
if (source.hasArray()) {
|
||||
request.source(source.array(), source.arrayOffset(), source.length());
|
||||
} else {
|
||||
request.source(source.toBytes());
|
||||
}
|
||||
IndexResponse response = client.index(request);
|
||||
watchRecord.version(response.getVersion());
|
||||
logger.debug("successfully updated watch record [{}]", watchRecord);
|
||||
} catch (IOException e) {
|
||||
throw new HistoryException("failed to update watch record [" + watchRecord + "]", e);
|
||||
} finally {
|
||||
putUpdateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tries to load all watch records that await execution. If for some reason the records could not be
|
||||
* loaded (e.g. the not all primary shards of the history index are active), returns {@code null}.
|
||||
*/
|
||||
public Collection<WatchRecord> loadRecords(ClusterState state, WatchRecord.State recordState) {
|
||||
String[] indices = state.metaData().concreteIndices(IndicesOptions.lenientExpandOpen(), INDEX_PREFIX + "*");
|
||||
if (indices.length == 0) {
|
||||
logger.debug("no .watch_history indices found. skipping loading awaiting watch records");
|
||||
templateUtils.putTemplate(INDEX_TEMPLATE_NAME, customIndexSettings);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
int numPrimaryShards = 0;
|
||||
for (String index : indices) {
|
||||
IndexMetaData indexMetaData = state.getMetaData().index(index);
|
||||
if (indexMetaData != null) {
|
||||
if (!state.routingTable().index(index).allPrimaryShardsActive()) {
|
||||
logger.debug("not all primary shards of the [{}] index are started.", index);
|
||||
throw new HistoryException("not all primary shards of the [{}] index are started.", index);
|
||||
} else {
|
||||
numPrimaryShards += indexMetaData.numberOfShards();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RefreshResponse refreshResponse = client.refresh(new RefreshRequest(INDEX_PREFIX + "*"));
|
||||
if (refreshResponse.getSuccessfulShards() < numPrimaryShards) {
|
||||
throw new HistoryException("refresh was supposed to run on [{}] shards, but ran on [{}] shards", numPrimaryShards, refreshResponse.getSuccessfulShards());
|
||||
}
|
||||
|
||||
SearchRequest searchRequest = createScanSearchRequest(recordState);
|
||||
SearchResponse response = client.search(searchRequest);
|
||||
List<WatchRecord> records = new ArrayList<>();
|
||||
try {
|
||||
if (response.getTotalShards() != response.getSuccessfulShards()) {
|
||||
throw new HistoryException("scan search was supposed to run on [{}] shards, but ran on [{}] shards", numPrimaryShards, response.getSuccessfulShards());
|
||||
}
|
||||
|
||||
if (response.getHits().getTotalHits() > 0) {
|
||||
response = client.searchScroll(response.getScrollId(), scrollTimeout);
|
||||
while (response.getHits().hits().length != 0) {
|
||||
for (SearchHit sh : response.getHits()) {
|
||||
String id = sh.getId();
|
||||
try {
|
||||
WatchRecord record = recordParser.parse(id, sh.version(), sh.getSourceRef());
|
||||
assert record.state() == recordState;
|
||||
logger.debug("loaded watch record [{}/{}/{}]", sh.index(), sh.type(), sh.id());
|
||||
records.add(record);
|
||||
} catch (Exception e) {
|
||||
logger.error("couldn't load watch record [{}], ignoring it...", e, id);
|
||||
}
|
||||
}
|
||||
response = client.searchScroll(response.getScrollId(), scrollTimeout);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
client.clearScroll(response.getScrollId());
|
||||
}
|
||||
templateUtils.putTemplate(INDEX_TEMPLATE_NAME, customIndexSettings);
|
||||
return records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the correct history index name for a given time
|
||||
*/
|
||||
|
@ -371,18 +177,4 @@ public class HistoryStore extends AbstractComponent implements NodeSettingsServi
|
|||
return INDEX_PREFIX + indexTimeFormat.print(time);
|
||||
}
|
||||
|
||||
private SearchRequest createScanSearchRequest(WatchRecord.State recordState) {
|
||||
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
|
||||
.query(QueryBuilders.termQuery(WatchRecord.Field.STATE.getPreferredName(), recordState.id()))
|
||||
.size(scrollSize)
|
||||
.version(true);
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest(INDEX_PREFIX + "*");
|
||||
searchRequest.source(sourceBuilder);
|
||||
searchRequest.searchType(SearchType.SCAN);
|
||||
searchRequest.types(DOC_TYPE);
|
||||
searchRequest.scroll(scrollTimeout);
|
||||
searchRequest.preference("_primary");
|
||||
return searchRequest;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.watcher.history;
|
||||
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
|
||||
public class TriggeredWatchException extends WatcherException {
|
||||
|
||||
public TriggeredWatchException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
public TriggeredWatchException(String msg, Throwable cause, Object... args) {
|
||||
super(msg, cause, args);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,66 +5,61 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.history;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.support.validation.WatcherSettingsException;
|
||||
import org.elasticsearch.watcher.actions.ActionRegistry;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.condition.ConditionRegistry;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.execution.*;
|
||||
import org.elasticsearch.watcher.input.ExecutableInput;
|
||||
import org.elasticsearch.watcher.input.InputRegistry;
|
||||
import org.elasticsearch.watcher.transform.TransformRegistry;
|
||||
import org.elasticsearch.watcher.trigger.TriggerEvent;
|
||||
import org.elasticsearch.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.watcher.watch.Watch;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionResult;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class WatchRecord implements ToXContent {
|
||||
|
||||
private Wid id;
|
||||
private String watchId;
|
||||
private TriggerEvent triggerEvent;
|
||||
private ExecutableInput input;
|
||||
private Condition condition;
|
||||
private State state;
|
||||
private WatchExecutionResult execution;
|
||||
private final Wid id;
|
||||
private final TriggerEvent triggerEvent;
|
||||
private final ExecutionState state;
|
||||
|
||||
private @Nullable String message;
|
||||
private @Nullable Map<String,Object> metadata;
|
||||
private final @Nullable ExecutableInput input;
|
||||
private final @Nullable Condition condition;
|
||||
private final @Nullable Map<String,Object> metadata;
|
||||
|
||||
// Used for assertion purposes, so we can ensure/test what we have loaded in memory is the same as what is persisted.
|
||||
private transient long version;
|
||||
private final @Nullable String message;
|
||||
private final @Nullable WatchExecutionResult execution;
|
||||
|
||||
private final AtomicBoolean sealed = new AtomicBoolean(false);
|
||||
|
||||
WatchRecord() {
|
||||
public WatchRecord(Wid id, TriggerEvent triggerEvent, String message, ExecutionState state) {
|
||||
this.id = id;
|
||||
this.triggerEvent = triggerEvent;
|
||||
this.execution = null;
|
||||
this.state = state;
|
||||
this.message = message;
|
||||
this.condition = null;
|
||||
this.input = null;
|
||||
this.metadata = null;
|
||||
}
|
||||
|
||||
public WatchRecord(Wid id, Watch watch, TriggerEvent triggerEvent) {
|
||||
this.id = id;
|
||||
this.watchId = watch.id();
|
||||
this.triggerEvent = triggerEvent;
|
||||
this.condition = watch.condition().condition();
|
||||
this.input = watch.input();
|
||||
this.state = State.AWAITS_EXECUTION;
|
||||
this.metadata = watch.metadata();
|
||||
this.version = 1;
|
||||
public WatchRecord(WatchExecutionContext context, WatchExecutionResult execution) {
|
||||
this.id = context.id();
|
||||
this.triggerEvent = context.triggerEvent();
|
||||
this.condition = context.watch().condition().condition();
|
||||
this.input = context.watch().input();
|
||||
this.execution = execution;
|
||||
this.metadata = context.watch().metadata();
|
||||
this.message = null;
|
||||
|
||||
if (!this.execution.conditionResult().met()) {
|
||||
state = ExecutionState.EXECUTION_NOT_NEEDED;
|
||||
} else {
|
||||
if (this.execution.actionsResults().throttled()) {
|
||||
state = ExecutionState.THROTTLED;
|
||||
} else {
|
||||
state = ExecutionState.EXECUTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Wid id() {
|
||||
|
@ -76,7 +71,7 @@ public class WatchRecord implements ToXContent {
|
|||
}
|
||||
|
||||
public String watchId() {
|
||||
return watchId;
|
||||
return id.watchId();
|
||||
}
|
||||
|
||||
public ExecutableInput input() { return input; }
|
||||
|
@ -85,7 +80,7 @@ public class WatchRecord implements ToXContent {
|
|||
return condition;
|
||||
}
|
||||
|
||||
public State state() {
|
||||
public ExecutionState state() {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -97,51 +92,28 @@ public class WatchRecord implements ToXContent {
|
|||
return metadata;
|
||||
}
|
||||
|
||||
public long version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
void version(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public WatchExecutionResult execution() {
|
||||
return execution;
|
||||
}
|
||||
|
||||
public void update(State state, @Nullable String message) {
|
||||
this.state = state;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void seal(WatchExecutionResult execution) {
|
||||
assert sealed.compareAndSet(false, true) : "sealing a watch record should only be done once";
|
||||
this.execution = execution;
|
||||
if (!execution.conditionResult().met()) {
|
||||
state = State.EXECUTION_NOT_NEEDED;
|
||||
} else {
|
||||
if (execution.actionsResults().throttled()) {
|
||||
state = State.THROTTLED;
|
||||
} else {
|
||||
state = State.EXECUTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Field.WATCH_ID.getPreferredName(), watchId);
|
||||
builder.field(Field.WATCH_ID.getPreferredName(), id.watchId());
|
||||
builder.startObject(Field.TRIGGER_EVENT.getPreferredName())
|
||||
.field(triggerEvent.type(), triggerEvent, params)
|
||||
.endObject();
|
||||
builder.field(Field.STATE.getPreferredName(), state.id());
|
||||
if (input != null) {
|
||||
builder.startObject(Watch.Field.INPUT.getPreferredName())
|
||||
.field(input.type(), input, params)
|
||||
.endObject();
|
||||
}
|
||||
if (condition != null) {
|
||||
builder.startObject(Watch.Field.CONDITION.getPreferredName())
|
||||
.field(condition.type(), condition, params)
|
||||
.endObject();
|
||||
builder.field(Field.STATE.getPreferredName(), state.id());
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
builder.field(Field.MESSAGE.getPreferredName(), message);
|
||||
|
@ -179,111 +151,6 @@ public class WatchRecord implements ToXContent {
|
|||
return id.toString();
|
||||
}
|
||||
|
||||
public enum State {
|
||||
|
||||
AWAITS_EXECUTION,
|
||||
CHECKING,
|
||||
EXECUTION_NOT_NEEDED,
|
||||
THROTTLED,
|
||||
EXECUTED,
|
||||
FAILED,
|
||||
DELETED_WHILE_QUEUED;
|
||||
|
||||
public String id() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public static State resolve(String id) {
|
||||
try {
|
||||
return valueOf(id.toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new HistoryException("unknown watch record state [{}]", id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Parser extends AbstractComponent {
|
||||
|
||||
private final ConditionRegistry conditionRegistry;
|
||||
private final ActionRegistry actionRegistry;
|
||||
private final InputRegistry inputRegistry;
|
||||
private final TransformRegistry transformRegistry;
|
||||
private final TriggerService triggerService;
|
||||
|
||||
@Inject
|
||||
public Parser(Settings settings, ConditionRegistry conditionRegistry, ActionRegistry actionRegistry,
|
||||
InputRegistry inputRegistry, TransformRegistry transformRegistry, TriggerService triggerService) {
|
||||
super(settings);
|
||||
this.conditionRegistry = conditionRegistry;
|
||||
this.actionRegistry = actionRegistry;
|
||||
this.inputRegistry = inputRegistry;
|
||||
this.transformRegistry = transformRegistry;
|
||||
this.triggerService = triggerService;
|
||||
}
|
||||
|
||||
public WatchRecord parse(String id, long version, BytesReference source) {
|
||||
try (XContentParser parser = XContentHelper.createParser(source)) {
|
||||
return parse(id, version, parser);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("unable to parse watch record", e);
|
||||
}
|
||||
}
|
||||
|
||||
public WatchRecord parse(String id, long version, XContentParser parser) throws IOException {
|
||||
WatchRecord record = new WatchRecord();
|
||||
record.id = new Wid(id);
|
||||
record.version = version;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.START_OBJECT;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Watch.Field.INPUT.match(currentFieldName)) {
|
||||
record.input = inputRegistry.parse(id, parser);
|
||||
} else if (Watch.Field.CONDITION.match(currentFieldName)) {
|
||||
record.condition = conditionRegistry.parseCondition(id, parser);
|
||||
} else if (Field.METADATA.match(currentFieldName)) {
|
||||
record.metadata = parser.map();
|
||||
} else if (Field.EXECUTION_RESULT.match(currentFieldName)) {
|
||||
record.execution = WatchExecutionResult.Parser.parse(record.id, parser, conditionRegistry, actionRegistry, inputRegistry, transformRegistry);
|
||||
} else if (Field.TRIGGER_EVENT.match(currentFieldName)) {
|
||||
record.triggerEvent = triggerService.parseTriggerEvent(record.watchId, id, parser);
|
||||
} else {
|
||||
throw new WatcherException("could not parse watch record [{}]. unexpected field [{}]", id, currentFieldName);
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if (Field.WATCH_ID.match(currentFieldName)) {
|
||||
record.watchId = parser.text();
|
||||
} else if (Field.MESSAGE.match(currentFieldName)) {
|
||||
record.message = parser.textOrNull();
|
||||
} else if (Field.STATE.match(currentFieldName)) {
|
||||
record.state = State.resolve(parser.text());
|
||||
} else {
|
||||
throw new WatcherException("could not parse watch record [{}]. unexpected field [{}]", id, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new WatcherException("could not parse watch record [{}]. unexpected token [{}] for [{}]", id, token, currentFieldName);
|
||||
}
|
||||
}
|
||||
|
||||
assert record.id() != null : "watch record [" + id +"] is missing watch_id";
|
||||
assert record.triggerEvent() != null : "watch record [" + id +"] is missing trigger";
|
||||
assert record.input() != null : "watch record [" + id +"] is missing input";
|
||||
assert record.condition() != null : "watch record [" + id +"] is condition input";
|
||||
assert record.state() != null : "watch record [" + id +"] is state input";
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Field {
|
||||
ParseField WATCH_ID = new ParseField("watch_id");
|
||||
ParseField TRIGGER_EVENT = new ParseField("trigger_event");
|
||||
|
|
|
@ -31,11 +31,6 @@ public abstract class InputFactory<I extends Input, R extends Input.Result, E ex
|
|||
*/
|
||||
public abstract I parseInput(String watchId, XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Parses the given xContent and creates a concrete result
|
||||
*/
|
||||
public abstract R parseResult(String watchId, XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates an executable input out of the given input.
|
||||
*/
|
||||
|
|
|
@ -63,23 +63,4 @@ public class InputRegistry {
|
|||
return input;
|
||||
}
|
||||
|
||||
public Input.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
String type = null;
|
||||
|
||||
XContentParser.Token token;
|
||||
Input.Result inputResult = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
type = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT && type != null) {
|
||||
InputFactory factory = factories.get(type);
|
||||
if (factory == null) {
|
||||
throw new InputException("could not parse input result for watch [{}]. unknown input type [{}]", watchId, type);
|
||||
}
|
||||
inputResult = factory.parseResult(watchId, parser);
|
||||
}
|
||||
}
|
||||
return inputResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -127,50 +127,6 @@ public class HttpInput implements Input {
|
|||
return builder.field(Field.REQUEST.getPreferredName(), request, params)
|
||||
.field(Field.STATUS.getPreferredName(), status);
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser, HttpRequest.Parser requestParser) throws IOException {
|
||||
HttpRequest sentRequest = null;
|
||||
Payload payload = null;
|
||||
int httpStatus = -1;
|
||||
|
||||
XContentParser.Token token;
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.REQUEST.match(currentFieldName)) {
|
||||
try {
|
||||
sentRequest = requestParser.parse(parser);
|
||||
} catch (HttpRequest.Parser.ParseException pe) {
|
||||
throw new HttpInputException("could not parse [{}] input result for watch [{}]. failed parsing [{}] field", pe, TYPE, watchId, Field.REQUEST.getPreferredName());
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Field.PAYLOAD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else {
|
||||
throw new HttpInputException("could not parse [{}] input result for watch [{}]. unexpected object field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||
if (Field.STATUS.match(currentFieldName)) {
|
||||
httpStatus = parser.intValue();
|
||||
} else {
|
||||
throw new HttpInputException("could not parse [{}] input result for watch [{}]. unexpected numeric field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new HttpInputException("could not parse [{}] input result for watch [{}]. unexpected token [{}]", TYPE, watchId, token);
|
||||
}
|
||||
}
|
||||
|
||||
if (sentRequest == null) {
|
||||
throw new HttpInputException("could not parse [{}] input result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.REQUEST.getPreferredName());
|
||||
}
|
||||
|
||||
if (httpStatus < 0) {
|
||||
throw new HttpInputException("could not parse [{}] input result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.STATUS.getPreferredName());
|
||||
}
|
||||
|
||||
return new HttpInput.Result(payload, sentRequest, httpStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Input.Builder<HttpInput> {
|
||||
|
|
|
@ -46,11 +46,6 @@ public final class HttpInputFactory extends InputFactory<HttpInput, HttpInput.Re
|
|||
return HttpInput.parse(watchId, parser, requestTemplateParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpInput.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return HttpInput.Result.parse(watchId, parser, requestParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableHttpInput createExecutable(HttpInput input) {
|
||||
return new ExecutableHttpInput(input, inputLogger, httpClient, templateEngine);
|
||||
|
|
|
@ -64,16 +64,6 @@ public class NoneInput implements Input {
|
|||
protected XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
|
||||
throw new NoneInputException("could not parse [{}] input result for watch [{}]. expected an empty object but found [{}] instead", TYPE, watchId, parser.currentToken());
|
||||
}
|
||||
if (parser.nextToken() != XContentParser.Token.END_OBJECT) {
|
||||
throw new NoneInputException("could not parse [{}] input result for watch [{}]. expected an empty object but found [{}] instead", TYPE, watchId, parser.currentToken());
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Input.Builder<NoneInput> {
|
||||
|
|
|
@ -33,11 +33,6 @@ public class NoneInputFactory extends InputFactory<NoneInput, NoneInput.Result,
|
|||
return NoneInput.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NoneInput.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return NoneInput.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableNoneInput createExecutable(NoneInput input) {
|
||||
return new ExecutableNoneInput(inputLogger);
|
||||
|
|
|
@ -143,41 +143,6 @@ public class SearchInput implements Input {
|
|||
WatcherUtils.writeSearchRequest(request, builder, params);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
SearchRequest executedRequest = null;
|
||||
Payload payload = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.REQUEST.match(currentFieldName)) {
|
||||
try {
|
||||
executedRequest = WatcherUtils.readSearchRequest(parser, ExecutableSearchInput.DEFAULT_SEARCH_TYPE);
|
||||
} catch (SearchRequestParseException srpe) {
|
||||
throw new SearchInputException("could not parse [{}] input result for watch [{}]. failed to parse [{}]", srpe, TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) {
|
||||
if (Field.PAYLOAD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else {
|
||||
throw new SearchInputException("could not parse [{}] input result for watch [{}]. unexpected field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (executedRequest == null) {
|
||||
throw new SearchInputException("could not parse [{}] input result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.REQUEST.getPreferredName());
|
||||
}
|
||||
|
||||
if (payload == null) {
|
||||
throw new SearchInputException("could not parse [{}] input result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.PAYLOAD.getPreferredName());
|
||||
}
|
||||
|
||||
return new Result(executedRequest, payload);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Input.Builder<SearchInput> {
|
||||
|
|
|
@ -38,11 +38,6 @@ public class SearchInputFactory extends InputFactory<SearchInput, SearchInput.Re
|
|||
return SearchInput.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchInput.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return SearchInput.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableSearchInput createExecutable(SearchInput input) {
|
||||
return new ExecutableSearchInput(input, inputLogger, client);
|
||||
|
|
|
@ -76,30 +76,6 @@ public class SimpleInput implements Input {
|
|||
protected XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
Payload payload = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) {
|
||||
if (Field.PAYLOAD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else {
|
||||
throw new SimpleInputException("could not parse [{}] input result for watch [{}]. unexpected field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (payload == null) {
|
||||
throw new SimpleInputException("could not parse [{}] input result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.PAYLOAD.getPreferredName());
|
||||
}
|
||||
|
||||
return new Result(payload);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Input.Builder<SimpleInput> {
|
||||
|
|
|
@ -33,11 +33,6 @@ public class SimpleInputFactory extends InputFactory<SimpleInput, SimpleInput.Re
|
|||
return SimpleInput.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleInput.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return SimpleInput.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableSimpleInput createExecutable(SimpleInput input) {
|
||||
return new ExecutableSimpleInput(input, inputLogger);
|
||||
|
|
|
@ -44,7 +44,7 @@ public class TemplateUtils extends AbstractComponent {
|
|||
}
|
||||
final byte[] template = Streams.copyToByteArray(is);
|
||||
PutIndexTemplateRequest request = new PutIndexTemplateRequest(templateName).source(template);
|
||||
if (customSettings != null) {
|
||||
if (customSettings != null && customSettings.names().size() > 0) {
|
||||
Settings updatedSettings = ImmutableSettings.builder()
|
||||
.put(request.settings())
|
||||
.put(customSettings)
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.init.proxy;
|
||||
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
|
@ -18,6 +17,8 @@ import org.elasticsearch.action.delete.DeleteResponse;
|
|||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.*;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.AdminClient;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
|
@ -63,6 +64,10 @@ public class ClientProxy implements InitializingService.Initializable {
|
|||
return client.index(preProcess(request)).actionGet();
|
||||
}
|
||||
|
||||
public UpdateResponse update(UpdateRequest request) {
|
||||
return client.update(preProcess(request)).actionGet();
|
||||
}
|
||||
|
||||
public BulkResponse bulk(BulkRequest request) {
|
||||
request.listenerThreaded(true);
|
||||
return client.bulk(preProcess(request)).actionGet();
|
||||
|
@ -78,8 +83,8 @@ public class ClientProxy implements InitializingService.Initializable {
|
|||
client.bulk(preProcess(request), listener);
|
||||
}
|
||||
|
||||
public ActionFuture<DeleteResponse> delete(DeleteRequest request) {
|
||||
return client.delete(preProcess(request));
|
||||
public DeleteResponse delete(DeleteRequest request) {
|
||||
return client.delete(preProcess(request)).actionGet();
|
||||
}
|
||||
|
||||
public SearchResponse search(SearchRequest request) {
|
||||
|
|
|
@ -31,11 +31,6 @@ public abstract class TransformFactory<T extends Transform, R extends Transform.
|
|||
*/
|
||||
public abstract T parseTransform(String watchId, XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Parses the given xcontent and creates a concrete transform result
|
||||
*/
|
||||
public abstract R parseResult(String watchId, XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates an executable transform out of the given transform.
|
||||
*/
|
||||
|
|
|
@ -57,26 +57,4 @@ public class TransformRegistry {
|
|||
}
|
||||
return factory.parseTransform(watchId, parser);
|
||||
}
|
||||
|
||||
public Transform.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
String type = null;
|
||||
XContentParser.Token token;
|
||||
Transform.Result result = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
type = parser.currentName();
|
||||
} else if (type != null) {
|
||||
result = parseResult(watchId, type, parser);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Transform.Result parseResult(String watchId, String type, XContentParser parser) throws IOException {
|
||||
TransformFactory factory = factories.get(type);
|
||||
if (factory == null) {
|
||||
throw new TransformException("could not parse transform result for watch [{}]. unknown transform type [{}]", watchId, type);
|
||||
}
|
||||
return factory.parseResult(watchId, parser);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,46 +114,6 @@ public class ChainTransform implements Transform {
|
|||
}
|
||||
return builder.endArray();
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser, TransformRegistry transformRegistry) throws IOException {
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new ChainTransformException("could not parse [{}] transform result for watch [{}]. expected an object, but found [{}] instead", TYPE, watchId, token);
|
||||
}
|
||||
|
||||
Payload payload = null;
|
||||
ImmutableList.Builder<Transform.Result> results = ImmutableList.builder();
|
||||
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else {
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Field.PAYLOAD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else {
|
||||
throw new ChainTransformException("could not parse [{}] transform result for watch [{}]. unexpected object field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
if (Field.RESULTS.match(currentFieldName)) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
results.add(transformRegistry.parseResult(watchId, parser));
|
||||
} else {
|
||||
throw new ChainTransformException("could not parse [{}] transform result for watch [{}]. expected an object representing a transform result, but found [{}] instead", TYPE, watchId, token);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ChainTransformException("could not parse [{}] transform result for watch [{}]. unexpected array field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new ChainTransformException("could not parse [{}] transform result for watch [{}]. unexpected token [{}]", TYPE, watchId, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ChainTransform.Result(payload, results.build());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Transform.Builder<ChainTransform> {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.watcher.transform.chain;
|
||||
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Injector;
|
||||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -61,11 +60,6 @@ public class ChainTransformFactory extends TransformFactory<ChainTransform, Chai
|
|||
return ChainTransform.parse(watchId, parser, registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainTransform.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return ChainTransform.Result.parse(watchId, parser, registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableChainTransform createExecutable(ChainTransform chainTransform) {
|
||||
ImmutableList.Builder<ExecutableTransform> executables = ImmutableList.builder();
|
||||
|
|
|
@ -78,22 +78,6 @@ public class ScriptTransform implements Transform {
|
|||
protected XContentBuilder xContentBody(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new ScriptTransformException("could not parse [{}] transform result for watch [{}]. expected an object, but found [{}] instead", TYPE, watchId, token);
|
||||
}
|
||||
token = parser.nextToken();
|
||||
if (token != XContentParser.Token.FIELD_NAME || !Field.PAYLOAD.match(parser.currentName())) {
|
||||
throw new ScriptTransformException("could not parse [{}] transform result for watch [{}]. expected a [{}] object, but found [{}] instead", TYPE, watchId, Field.PAYLOAD.getPreferredName(), token);
|
||||
}
|
||||
token = parser.nextToken();
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new ScriptTransformException("could not parse [{}] transform result for watch [{}]. expected a [{}] object, but found [{}] instead", TYPE, watchId, Field.PAYLOAD.getPreferredName(), token);
|
||||
}
|
||||
return new ScriptTransform.Result(new Payload.XContent(parser));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Transform.Builder<ScriptTransform> {
|
||||
|
|
|
@ -37,11 +37,6 @@ public class ScriptTransformFactory extends TransformFactory<ScriptTransform, Sc
|
|||
return ScriptTransform.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptTransform.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return ScriptTransform.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableScriptTransform createExecutable(ScriptTransform transform) {
|
||||
return new ExecutableScriptTransform(transform, transformLogger, scriptService);
|
||||
|
|
|
@ -91,41 +91,6 @@ public class SearchTransform implements Transform {
|
|||
WatcherUtils.writeSearchRequest(request, builder, params);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static Result parse(String watchId, XContentParser parser) throws IOException {
|
||||
SearchRequest executedRequest = null;
|
||||
Payload payload = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.REQUEST.match(currentFieldName)) {
|
||||
try {
|
||||
executedRequest = WatcherUtils.readSearchRequest(parser, ExecutableSearchTransform.DEFAULT_SEARCH_TYPE);
|
||||
} catch (SearchRequestParseException srpe) {
|
||||
throw new SearchTransformException("could not parse [{}] transform result for watch [{}]. failed to parse [{}]", srpe, TYPE, watchId, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) {
|
||||
if (Field.PAYLOAD.match(currentFieldName)) {
|
||||
payload = new Payload.XContent(parser);
|
||||
} else {
|
||||
throw new SearchTransformException("could not parse [{}] transform result for watch [{}]. unexpected field [{}]", TYPE, watchId, currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (payload == null) {
|
||||
throw new SearchTransformException("could not parse [{}] transform result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.PAYLOAD.getPreferredName());
|
||||
}
|
||||
|
||||
if (executedRequest == null) {
|
||||
throw new SearchTransformException("could not parse [{}] transform result for watch [{}]. missing required [{}] field", TYPE, watchId, Field.REQUEST.getPreferredName());
|
||||
}
|
||||
|
||||
return new SearchTransform.Result(executedRequest, payload);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Transform.Builder<SearchTransform> {
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.elasticsearch.common.logging.Loggers;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
import org.elasticsearch.watcher.transform.TransformFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -38,11 +37,6 @@ public class SearchTransformFactory extends TransformFactory<SearchTransform, Se
|
|||
return SearchTransform.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchTransform.Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
return SearchTransform.Result.parse(watchId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableSearchTransform createExecutable(SearchTransform transform) {
|
||||
return new ExecutableSearchTransform(transform, transformLogger, client);
|
||||
|
|
|
@ -16,6 +16,8 @@ import org.elasticsearch.action.index.IndexResponse;
|
|||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
|
@ -25,6 +27,8 @@ import org.elasticsearch.common.lucene.uid.Versions;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
|
@ -139,26 +143,26 @@ public class WatchStore extends AbstractComponent {
|
|||
* Updates and persists the status of the given watch
|
||||
*/
|
||||
public void updateStatus(Watch watch) throws IOException {
|
||||
ensureStarted();
|
||||
// at the moment we store the status together with the watch,
|
||||
// so we just need to update the watch itself
|
||||
// TODO: consider storing the status in a different documment (watch_status doc) (must smaller docs... faster for frequent updates)
|
||||
if (watch.status().dirty()) {
|
||||
update(watch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates and persists the given watch
|
||||
*/
|
||||
void update(Watch watch) throws IOException {
|
||||
ensureStarted();
|
||||
BytesReference source = JsonXContent.contentBuilder().value(watch).bytes();
|
||||
IndexResponse response = client.index(createIndexRequest(watch.id(), source, watch.version()));
|
||||
XContentBuilder source = JsonXContent.contentBuilder().
|
||||
startObject()
|
||||
.field(Watch.Field.STATUS.getPreferredName(), watch.status(), ToXContent.EMPTY_PARAMS)
|
||||
.endObject();
|
||||
UpdateRequest updateRequest = new UpdateRequest(INDEX, DOC_TYPE, watch.id());
|
||||
updateRequest.listenerThreaded(false);
|
||||
updateRequest.doc(source);
|
||||
updateRequest.version(watch.version());
|
||||
UpdateResponse response = client.update(updateRequest);
|
||||
watch.status().version(response.getVersion());
|
||||
watch.version(response.getVersion());
|
||||
watch.status().resetDirty();
|
||||
// Don't need to update the watches, since we are working on an instance from it.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the watch with the specified id if exists
|
||||
|
@ -172,7 +176,11 @@ public class WatchStore extends AbstractComponent {
|
|||
if (watch != null && !force) {
|
||||
request.version(watch.version());
|
||||
}
|
||||
DeleteResponse response = client.delete(request).actionGet();
|
||||
DeleteResponse response = client.delete(request);
|
||||
// Another operation may hold the Watch instance, so lets set the version for consistency:
|
||||
if (watch != null) {
|
||||
watch.version(response.getVersion());
|
||||
}
|
||||
return new WatchDelete(response);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"template": ".triggered_watches",
|
||||
"order": 2147483647,
|
||||
"settings": {
|
||||
"index.number_of_shards": 1,
|
||||
"index.mapper.dynamic" : false,
|
||||
"index.refresh_interval" : "-1"
|
||||
},
|
||||
"mappings": {
|
||||
"triggered_watch": {
|
||||
"dynamic" : "strict",
|
||||
"_all" : {
|
||||
"enabled" : false
|
||||
},
|
||||
"properties": {
|
||||
"trigger_event": {
|
||||
"type": "object",
|
||||
"dynamic": true,
|
||||
"enabled" : false,
|
||||
"properties": {
|
||||
"schedule": {
|
||||
"type": "object",
|
||||
"dynamic": true,
|
||||
"properties": {
|
||||
"triggered_time": {
|
||||
"type": "date"
|
||||
},
|
||||
"scheduled_time": {
|
||||
"type": "date"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"index": "not_analyzed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,11 +13,9 @@ import org.elasticsearch.common.joda.time.DateTime;
|
|||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
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.json.JsonXContent;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.email.service.*;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
|
@ -30,7 +28,6 @@ import org.elasticsearch.watcher.watch.Payload;
|
|||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
|
@ -366,117 +363,6 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
.parseExecutable(randomAsciiOfLength(3), randomAsciiOfLength(7), parser);
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 20)
|
||||
public void testParser_Result() throws Exception {
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now(UTC));
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
Action.Result.Status status = randomFrom(Action.Result.Status.values());
|
||||
Email email = Email.builder().id("_id")
|
||||
.from(new Email.Address("from@domain"))
|
||||
.to(Email.AddressList.parse("to@domain"))
|
||||
.sentDate(new DateTime(UTC))
|
||||
.subject("_subject")
|
||||
.textBody("_text_body")
|
||||
.build();
|
||||
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("status", status.name().toLowerCase(Locale.ROOT));
|
||||
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
builder.field("email", email);
|
||||
builder.field("account", "_account");
|
||||
break;
|
||||
case FAILURE:
|
||||
builder.field("reason", "failure_reason");
|
||||
break;
|
||||
case THROTTLED:
|
||||
builder.field("reason", "throttle_reason");
|
||||
break;
|
||||
case SIMULATED:
|
||||
builder.field("email", email);
|
||||
break;
|
||||
default:
|
||||
throw new WatcherException("unsupported action result status [{}]", status.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
BytesReference bytes = builder.bytes();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
EmailService service = mock(EmailService.class);
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
Action.Result result = new EmailActionFactory(ImmutableSettings.EMPTY, service, engine)
|
||||
.parseResult(wid, actionId, parser);
|
||||
|
||||
assertThat(result.status(), is(status));
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
assertThat(result, instanceOf(EmailAction.Result.Success.class));
|
||||
assertThat(((EmailAction.Result.Success) result).email(), equalTo(email));
|
||||
assertThat(((EmailAction.Result.Success) result).account(), is("_account"));
|
||||
break;
|
||||
case FAILURE:
|
||||
assertThat(result, instanceOf(Action.Result.Failure.class));
|
||||
assertThat(((Action.Result.Failure) result).reason(), equalTo("failure_reason"));
|
||||
break;
|
||||
case THROTTLED:
|
||||
assertThat(result, instanceOf(Action.Result.Throttled.class));
|
||||
assertThat(((Action.Result.Throttled) result).reason(), equalTo("throttle_reason"));
|
||||
break;
|
||||
case SIMULATED:
|
||||
assertThat(result, instanceOf(EmailAction.Result.Simulated.class));
|
||||
assertThat(((EmailAction.Result.Simulated) result).email(), equalTo(email));
|
||||
break;
|
||||
default:
|
||||
throw new WatcherException("unsupported action result status [{}]", status.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_Result_Simulated_SelfGenerated() throws Exception {
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now(UTC));
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
Email email = Email.builder().id("_id")
|
||||
.from(new Email.Address("from@domain"))
|
||||
.to(Email.AddressList.parse("to@domain"))
|
||||
.sentDate(new DateTime(UTC))
|
||||
.subject("_subject")
|
||||
.textBody("_text_body")
|
||||
.build();
|
||||
|
||||
EmailAction.Result.Simulated simulatedResult = new EmailAction.Result.Simulated(email);
|
||||
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
simulatedResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
BytesReference bytes = builder.bytes();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
Action.Result result = new EmailActionFactory(ImmutableSettings.EMPTY, mock(EmailService.class), mock(TemplateEngine.class))
|
||||
.parseResult(wid, actionId, parser);
|
||||
|
||||
assertThat(result, instanceOf(EmailAction.Result.Simulated.class));
|
||||
assertThat(((EmailAction.Result.Simulated) result).email(), equalTo(email));
|
||||
}
|
||||
|
||||
@Test(expected = EmailActionException.class)
|
||||
public void testParser_Result_Invalid() throws Exception {
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now(UTC));
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("unknown_field", "value")
|
||||
.endObject();
|
||||
BytesReference bytes = builder.bytes();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
new EmailActionFactory(ImmutableSettings.EMPTY, mock(EmailService.class), mock(TemplateEngine.class))
|
||||
.parseResult(wid, actionId, parser);
|
||||
}
|
||||
|
||||
static DataAttachment randomDataAttachment() {
|
||||
return randomFrom(DataAttachment.JSON, DataAttachment.YAML, null);
|
||||
}
|
||||
|
|
|
@ -6,16 +6,11 @@
|
|||
package org.elasticsearch.watcher.actions.index;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
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.json.JsonXContent;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
|
@ -28,7 +23,6 @@ import org.elasticsearch.watcher.actions.email.service.EmailService;
|
|||
import org.elasticsearch.watcher.actions.email.service.Profile;
|
||||
import org.elasticsearch.watcher.execution.TriggeredExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
|
@ -40,14 +34,13 @@ import org.elasticsearch.watcher.watch.Watch;
|
|||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
|
@ -146,113 +139,4 @@ public class IndexActionTests extends ElasticsearchIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 30)
|
||||
public void testParser_Result() throws Exception {
|
||||
Status status = randomFrom(Status.SUCCESS, Status.SIMULATED, Status.FAILURE);
|
||||
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("status", status.name().toLowerCase(Locale.ROOT));
|
||||
|
||||
Payload.Simple source = null;
|
||||
String index = randomAsciiOfLength(5);
|
||||
String docType = randomAsciiOfLength(5);
|
||||
|
||||
switch (status) {
|
||||
|
||||
case SIMULATED:
|
||||
source = new Payload.Simple(ImmutableMap.<String, Object>builder()
|
||||
.put("data", new HashMap<String, Object>())
|
||||
.put("timestamp", DateTime.now(UTC).toString())
|
||||
.build());
|
||||
builder.startObject(IndexAction.Field.REQUEST.getPreferredName())
|
||||
.field(IndexAction.Field.INDEX.getPreferredName(), index)
|
||||
.field(IndexAction.Field.DOC_TYPE.getPreferredName(), docType)
|
||||
.field(IndexAction.Field.SOURCE.getPreferredName(), source)
|
||||
.endObject();
|
||||
break;
|
||||
|
||||
case SUCCESS:
|
||||
Map<String,Object> data = new HashMap<>();
|
||||
data.put("created", true);
|
||||
data.put("id", "0");
|
||||
data.put("version", 1);
|
||||
data.put("type", "test-type");
|
||||
data.put("index", "test-index");
|
||||
builder.field(IndexAction.Field.RESPONSE.getPreferredName(), data);
|
||||
break;
|
||||
|
||||
case FAILURE:
|
||||
builder.field("reason", "_reason");
|
||||
}
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(4), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
Action.Result result = new IndexActionFactory(ImmutableSettings.EMPTY, ClientProxy.of(client()))
|
||||
.parseResult(wid, actionId, parser);
|
||||
|
||||
|
||||
assertThat(result.status(), is(status));
|
||||
|
||||
switch (status) {
|
||||
|
||||
case SIMULATED:
|
||||
assertThat(result, instanceOf(IndexAction.Result.Simulated.class));
|
||||
assertThat(((IndexAction.Result.Simulated) result).source(), equalTo((Payload) source));
|
||||
assertThat(((IndexAction.Result.Simulated) result).index(), equalTo(index));
|
||||
assertThat(((IndexAction.Result.Simulated) result).docType(), equalTo(docType));
|
||||
break;
|
||||
|
||||
case SUCCESS:
|
||||
assertThat(result, instanceOf(IndexAction.Result.Success.class));
|
||||
Map<String, Object> responseData = ((IndexAction.Result.Success) result).response().data();
|
||||
assertThat(responseData.get("created"), equalTo((Object) Boolean.TRUE));
|
||||
assertThat(responseData.get("version"), equalTo((Object) 1));
|
||||
assertThat(responseData.get("type").toString(), equalTo("test-type"));
|
||||
assertThat(responseData.get("index").toString(), equalTo("test-index"));
|
||||
break;
|
||||
|
||||
default: // failure
|
||||
assertThat(result.status(), is(Status.FAILURE));
|
||||
assertThat(result, instanceOf(Action.Result.Failure.class));
|
||||
assertThat(((Action.Result.Failure) result).reason(), is("_reason"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_Result_Simulated_SelfGenerated() throws Exception {
|
||||
IndexRequest request = new IndexRequest("test-index").type("test-type");
|
||||
XContentBuilder resultBuilder = XContentFactory.jsonBuilder().prettyPrint();
|
||||
resultBuilder.startObject();
|
||||
resultBuilder.field("data", new HashMap<String, Object>());
|
||||
resultBuilder.field("timestamp", new DateTime(UTC));
|
||||
resultBuilder.endObject();
|
||||
request.source(resultBuilder);
|
||||
Payload.Simple requestPayload = new Payload.Simple(request.sourceAsMap());
|
||||
|
||||
String index = randomAsciiOfLength(4);
|
||||
String docType = randomAsciiOfLength(5);
|
||||
IndexAction.Result.Simulated simulatedResult = new IndexAction.Result.Simulated(index, docType, requestPayload);
|
||||
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
simulatedResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
BytesReference bytes = builder.bytes();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(4), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
Action.Result result = new IndexActionFactory(ImmutableSettings.EMPTY, ClientProxy.of(client()))
|
||||
.parseResult(wid, actionId, parser);
|
||||
|
||||
assertThat(result, instanceOf(IndexAction.Result.Simulated.class));
|
||||
assertThat(((IndexAction.Result.Simulated) result).source(), equalTo((Payload) requestPayload));
|
||||
assertThat(((IndexAction.Result.Simulated) result).index(), equalTo(index));
|
||||
assertThat(((IndexAction.Result.Simulated) result).docType(), equalTo(docType));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,15 +6,12 @@
|
|||
package org.elasticsearch.watcher.actions.logging;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
|
@ -22,17 +19,14 @@ import org.elasticsearch.watcher.actions.Action;
|
|||
import org.elasticsearch.watcher.actions.ActionException;
|
||||
import org.elasticsearch.watcher.actions.email.service.Attachment;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
import org.elasticsearch.watcher.test.WatcherTestUtils;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
|
@ -193,176 +187,6 @@ public class LoggingActionTests extends ElasticsearchTestCase {
|
|||
parser.parseExecutable(randomAsciiOfLength(5), randomAsciiOfLength(5), xContentParser);
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 30)
|
||||
public void testParser_Result_Success() throws Exception {
|
||||
Settings settings = ImmutableSettings.EMPTY;
|
||||
LoggingActionFactory parser = new LoggingActionFactory(settings, engine);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
String text = randomAsciiOfLength(10);
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("status", Action.Result.Status.SUCCESS.name().toLowerCase(Locale.ROOT))
|
||||
.field("logged_text", text)
|
||||
.endObject();
|
||||
|
||||
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
xContentParser.nextToken();
|
||||
|
||||
// will fail as there's no text
|
||||
Action.Result result = parser.parseResult(wid, actionId, xContentParser);
|
||||
assertThat(result, Matchers.notNullValue());
|
||||
assertThat(result.status(), is(Action.Result.Status.SUCCESS));
|
||||
assertThat(result, Matchers.instanceOf(LoggingAction.Result.Success.class));
|
||||
assertThat(((LoggingAction.Result.Success) result).loggedText(), is(text));
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 30)
|
||||
public void testParser_Result_Failure() throws Exception {
|
||||
Settings settings = ImmutableSettings.EMPTY;
|
||||
LoggingActionFactory parser = new LoggingActionFactory(settings, engine);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
String reason = randomAsciiOfLength(10);
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("status", Action.Result.Status.FAILURE.name().toLowerCase(Locale.ROOT))
|
||||
.field("reason", reason)
|
||||
.endObject();
|
||||
|
||||
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
xContentParser.nextToken();
|
||||
|
||||
// will fail as there's no text
|
||||
Action.Result result = parser.parseResult(wid, actionId, xContentParser);
|
||||
assertThat(result, Matchers.notNullValue());
|
||||
assertThat(result.status(), is(Action.Result.Status.FAILURE));
|
||||
assertThat(result, Matchers.instanceOf(Action.Result.Failure.class));
|
||||
assertThat(((Action.Result.Failure) result).reason(), is(reason));
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 30)
|
||||
public void testParser_Result_Simulated() throws Exception {
|
||||
Settings settings = ImmutableSettings.EMPTY;
|
||||
LoggingActionFactory parser = new LoggingActionFactory(settings, engine);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
String text = randomAsciiOfLength(10);
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("status", Action.Result.Status.SIMULATED.name().toLowerCase(Locale.ROOT))
|
||||
.field("logged_text", text)
|
||||
.endObject();
|
||||
|
||||
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
xContentParser.nextToken();
|
||||
|
||||
// will fail as there's no text
|
||||
Action.Result result = parser.parseResult(wid, actionId, xContentParser);
|
||||
assertThat(result, Matchers.notNullValue());
|
||||
assertThat(result.status(), is(Action.Result.Status.SIMULATED));
|
||||
assertThat(result, Matchers.instanceOf(LoggingAction.Result.Simulated.class));
|
||||
assertThat(((LoggingAction.Result.Simulated) result).loggedText(), is(text));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_Result_Simulated_SelfGenerated() throws Exception {
|
||||
Settings settings = ImmutableSettings.EMPTY;
|
||||
LoggingActionFactory actionParser = new LoggingActionFactory(settings, engine);
|
||||
String text = randomAsciiOfLength(10);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
LoggingAction.Result.Simulated simulatedResult = new LoggingAction.Result.Simulated(text);
|
||||
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
simulatedResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
BytesReference bytes = builder.bytes();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
|
||||
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
xContentParser.nextToken();
|
||||
|
||||
// will fail as there's no text
|
||||
Action.Result result = actionParser.parseResult(wid, actionId, xContentParser);
|
||||
assertThat(result, Matchers.notNullValue());
|
||||
assertThat(result.status(), is(Action.Result.Status.SIMULATED));
|
||||
assertThat(result, Matchers.instanceOf(LoggingAction.Result.Simulated.class));
|
||||
assertThat(((LoggingAction.Result.Simulated) result).loggedText(), is(text));
|
||||
}
|
||||
|
||||
@Test(expected = ActionException.class)
|
||||
public void testParser_Result_MissingStatusField() throws Exception {
|
||||
Settings settings = ImmutableSettings.EMPTY;
|
||||
LoggingActionFactory parser = new LoggingActionFactory(settings, engine);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
String text = randomAsciiOfLength(10);
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("logged_text", text)
|
||||
.endObject();
|
||||
|
||||
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
xContentParser.nextToken();
|
||||
|
||||
// will fail as there's no success boolean field
|
||||
parser.parseResult(wid, actionId, xContentParser);
|
||||
}
|
||||
|
||||
@Test(expected = ActionException.class)
|
||||
public void testParser_Result_Failure_WithoutReason() throws Exception {
|
||||
Settings settings = ImmutableSettings.EMPTY;
|
||||
LoggingActionFactory parser = new LoggingActionFactory(settings, engine);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
String text = randomAsciiOfLength(10);
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("status", Action.Result.Status.FAILURE);
|
||||
if (randomBoolean()) {
|
||||
builder.field("logged_text", text);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
xContentParser.nextToken();
|
||||
|
||||
// will fail as the reason field is missing for the failure result
|
||||
parser.parseResult(wid, actionId, xContentParser);
|
||||
}
|
||||
|
||||
@Test(expected = ActionException.class)
|
||||
public void testParser_Result_Success_WithoutLoggedText() throws Exception {
|
||||
Settings settings = ImmutableSettings.EMPTY;
|
||||
LoggingActionFactory parser = new LoggingActionFactory(settings, engine);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(3), randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
String text = randomAsciiOfLength(10);
|
||||
XContentBuilder builder = jsonBuilder().startObject()
|
||||
.field("success", true);
|
||||
if (randomBoolean()) {
|
||||
builder.field("reason", text);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
xContentParser.nextToken();
|
||||
|
||||
// will fail as the logged_text field is missing for the successful result
|
||||
parser.parseResult(wid, actionId, xContentParser);
|
||||
}
|
||||
|
||||
static void verifyLogger(ESLogger logger, LoggingLevel level, String text) {
|
||||
switch (level) {
|
||||
case ERROR:
|
||||
|
|
|
@ -6,26 +6,21 @@
|
|||
package org.elasticsearch.watcher.actions.webhook;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.Action.Result.Status;
|
||||
import org.elasticsearch.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.watcher.actions.email.service.*;
|
||||
import org.elasticsearch.watcher.execution.TriggeredExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.support.http.*;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthFactory;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
|
@ -48,7 +43,6 @@ import org.junit.Test;
|
|||
import javax.mail.internet.AddressException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
|
@ -229,170 +223,6 @@ public class WebhookActionTests extends ElasticsearchTestCase {
|
|||
fail("expected a WebhookActionException since we only provided either a host or a port but not both");
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 30)
|
||||
public void testParser_Result() throws Exception {
|
||||
String body = "_body";
|
||||
String host = "test.host";
|
||||
String path = "/_url";
|
||||
HttpMethod method = randomFrom(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD);
|
||||
|
||||
Wid wid = new Wid("_watch", randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
HttpRequest request = HttpRequest.builder(host, 123)
|
||||
.path(path)
|
||||
.body(body)
|
||||
.method(method)
|
||||
.build();
|
||||
|
||||
HttpResponse response = new HttpResponse(randomIntBetween(200, 599), randomAsciiOfLength(10).getBytes(UTF8));
|
||||
|
||||
Status status = randomFrom(Status.values());
|
||||
boolean responseError = status == Status.FAILURE && randomBoolean();
|
||||
|
||||
HttpClient client = status == Status.SUCCESS ? ExecuteScenario.Success.client() :
|
||||
responseError ? ExecuteScenario.ErrorCode.client() :
|
||||
status == Status.FAILURE ? ExecuteScenario.Error.client() : ExecuteScenario.NoExecute.client();
|
||||
|
||||
|
||||
WebhookActionFactory actionParser = webhookFactory(client);
|
||||
|
||||
XContentBuilder builder = jsonBuilder()
|
||||
.startObject()
|
||||
.field(WebhookAction.Field.STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT));
|
||||
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
builder.field(WebhookAction.Field.REQUEST.getPreferredName(), request);
|
||||
builder.field(WebhookAction.Field.RESPONSE.getPreferredName(), response);
|
||||
break;
|
||||
case SIMULATED:
|
||||
builder.field(WebhookAction.Field.REQUEST.getPreferredName(), request);
|
||||
break;
|
||||
case FAILURE:
|
||||
if (responseError) {
|
||||
builder.field(WebhookAction.Field.REASON.getPreferredName(), "status_code_failure_reason");
|
||||
builder.field(WebhookAction.Field.REQUEST.getPreferredName(), request);
|
||||
builder.field(WebhookAction.Field.RESPONSE.getPreferredName(), response);
|
||||
} else {
|
||||
builder.field(WebhookAction.Field.REASON.getPreferredName(), "failure_reason");
|
||||
}
|
||||
break;
|
||||
case THROTTLED:
|
||||
builder.field(WebhookAction.Field.REASON.getPreferredName(), "throttle_reason");
|
||||
break;
|
||||
default:
|
||||
throw new WatcherException("unsupported action result status [{}]", status.name());
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
Action.Result result = actionParser.parseResult(wid, actionId, parser);
|
||||
|
||||
assertThat(result.status(), is(status));
|
||||
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
assertThat(result, instanceOf(WebhookAction.Result.Success.class));
|
||||
WebhookAction.Result.Success success = (WebhookAction.Result.Success) result;
|
||||
assertThat(success.request(), equalTo(request));
|
||||
assertThat(success.response(), equalTo(response));
|
||||
break;
|
||||
case SIMULATED:
|
||||
assertThat(result, instanceOf(WebhookAction.Result.Simulated.class));
|
||||
WebhookAction.Result.Simulated simulated = (WebhookAction.Result.Simulated) result;
|
||||
assertThat(simulated.request(), equalTo(request));
|
||||
break;
|
||||
case FAILURE:
|
||||
if (responseError) {
|
||||
assertThat(result, instanceOf(WebhookAction.Result.Failure.class));
|
||||
WebhookAction.Result.Failure responseFailure = (WebhookAction.Result.Failure) result;
|
||||
assertThat(responseFailure.reason(), is("status_code_failure_reason"));
|
||||
assertThat(responseFailure.request(), equalTo(request));
|
||||
assertThat(responseFailure.response(), equalTo(response));
|
||||
} else {
|
||||
assertThat(result, instanceOf(Action.Result.Failure.class));
|
||||
Action.Result.Failure failure = (Action.Result.Failure) result;
|
||||
assertThat(failure.reason(), is("failure_reason"));
|
||||
}
|
||||
break;
|
||||
case THROTTLED:
|
||||
assertThat(result, instanceOf(Action.Result.Throttled.class));
|
||||
Action.Result.Throttled throttled = (Action.Result.Throttled) result;
|
||||
assertThat(throttled.reason(), is("throttle_reason"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 5)
|
||||
public void testParser_Result_Simulated() throws Exception {
|
||||
String body = "_body";
|
||||
String host = "test.host";
|
||||
String path = "/_url";
|
||||
HttpMethod method = randomFrom(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD);
|
||||
|
||||
Wid wid = new Wid("_watch", randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
HttpRequest request = HttpRequest.builder(host, 123)
|
||||
.path(path)
|
||||
.body(body)
|
||||
.method(method)
|
||||
.build();
|
||||
|
||||
XContentBuilder builder = jsonBuilder()
|
||||
.startObject()
|
||||
.field(Action.Field.STATUS.getPreferredName(), Status.SIMULATED.name().toLowerCase(Locale.ROOT))
|
||||
.field(WebhookAction.Field.REQUEST.getPreferredName(), request)
|
||||
.endObject();
|
||||
|
||||
HttpClient client = ExecuteScenario.Success.client();
|
||||
|
||||
WebhookActionFactory actionParser = webhookFactory(client);
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
Action.Result result = actionParser.parseResult(wid, actionId, parser);
|
||||
assertThat(result, instanceOf(WebhookAction.Result.Simulated.class));
|
||||
assertThat(((WebhookAction.Result.Simulated) result).request(), equalTo(request));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParser_Result_Simulated_SelfGenerated() throws Exception {
|
||||
String body = "_body";
|
||||
String host = "test.host";
|
||||
String path = "/_url";
|
||||
HttpMethod method = HttpMethod.GET;
|
||||
|
||||
HttpRequest request = HttpRequest.builder(host, 123)
|
||||
.path(path)
|
||||
.body(body)
|
||||
.method(method)
|
||||
.build();
|
||||
|
||||
Wid wid = new Wid("_watch", randomLong(), DateTime.now());
|
||||
String actionId = randomAsciiOfLength(5);
|
||||
|
||||
WebhookAction.Result.Simulated simulatedResult = new WebhookAction.Result.Simulated(request);
|
||||
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
simulatedResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
BytesReference bytes = builder.bytes();
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
|
||||
Action.Result result = webhookFactory(ExecuteScenario.Success.client())
|
||||
.parseResult(wid, actionId, parser);
|
||||
|
||||
assertThat(result, instanceOf(WebhookAction.Result.Simulated.class));
|
||||
assertThat(((WebhookAction.Result.Simulated)result).request(), equalTo(request));
|
||||
}
|
||||
|
||||
|
||||
private WebhookActionFactory webhookFactory(HttpClient client) {
|
||||
return new WebhookActionFactory(ImmutableSettings.EMPTY, client, new HttpRequest.Parser(authRegistry),
|
||||
new HttpRequestTemplate.Parser(authRegistry), templateEngine);
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.condition.ConditionFactory;
|
||||
import org.elasticsearch.watcher.condition.ExecutableCondition;
|
||||
import org.junit.Test;
|
||||
|
@ -54,33 +53,4 @@ public class AlwaysConditionTests extends ElasticsearchTestCase {
|
|||
+ AlwaysCondition.TYPE + "] condition should not parse with a body");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testResultParser_Valid() throws Exception {
|
||||
ConditionFactory factory = new AlwaysConditionFactory(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.endObject();
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
Condition.Result alwaysTrueResult = factory.parseResult("_id", parser);
|
||||
assertTrue(alwaysTrueResult.met());
|
||||
}
|
||||
|
||||
@Test(expected = AlwaysConditionException.class)
|
||||
public void testResultParser_Invalid() throws Exception {
|
||||
ConditionFactory factory = new AlwaysConditionFactory(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", false);
|
||||
builder.endObject();
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
factory.parseResult("_id", parser);
|
||||
fail("expected a condition exception trying to parse an invalid condition result XContent, ["
|
||||
+ AlwaysCondition.TYPE + "] condition result should not parse with a [met] field");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -242,43 +242,4 @@ public class CompareConditionTests extends ElasticsearchTestCase {
|
|||
factory.parseCondition("_id", parser);
|
||||
}
|
||||
|
||||
|
||||
@Test @Repeat(iterations = 10)
|
||||
public void testParse_Result_Valid() throws Exception {
|
||||
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
|
||||
|
||||
boolean met = randomBoolean();
|
||||
Object resolvedValue = randomFrom("1", 5, null, ImmutableList.of("1", "2"), ImmutableMap.of("key", "value"));
|
||||
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", met);
|
||||
builder.field("resolved_value", resolvedValue);
|
||||
builder.endObject();
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
CompareCondition.Result result = factory.parseResult("_id", parser);
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.met(), is(met));
|
||||
assertThat(result.getResolveValue(), is(resolvedValue));
|
||||
}
|
||||
|
||||
@Test(expected = CompareConditionException.class)
|
||||
public void testParse_Result_Invalid_MissingResolvedValue() throws Exception {
|
||||
CompareConditionFactory factory = new CompareConditionFactory(ImmutableSettings.EMPTY, SystemClock.INSTANCE);
|
||||
|
||||
boolean met = randomBoolean();
|
||||
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", met);
|
||||
builder.endObject();
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
factory.parseResult("_id", parser);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.condition.ConditionFactory;
|
||||
import org.elasticsearch.watcher.condition.ExecutableCondition;
|
||||
import org.elasticsearch.watcher.condition.always.AlwaysCondition;
|
||||
|
@ -57,32 +56,4 @@ public class NeverConditionTests extends ElasticsearchTestCase {
|
|||
+ AlwaysCondition.TYPE + "] condition should not parse with a body");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testResultParser_Valid() throws Exception {
|
||||
ConditionFactory factory = new NeverConditionFactory(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.endObject();
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
Condition.Result result = factory.parseResult("_id", parser);
|
||||
assertFalse(result.met());
|
||||
}
|
||||
|
||||
@Test(expected = NeverConditionException.class)
|
||||
public void testResultParser_Invalid() throws Exception {
|
||||
ConditionFactory factory = new NeverConditionFactory(ImmutableSettings.settingsBuilder().build());
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", false);
|
||||
builder.endObject();
|
||||
XContentParser parser = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
factory.parseResult("_id", parser);
|
||||
fail("expected a condition exception trying to parse an invalid condition result XContent, ["
|
||||
+ NeverCondition.TYPE + "] condition result should not parse with a [met] field");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.watcher.condition.script;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.carrotsearch.randomizedtesting.annotations.Seed;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
|
@ -15,7 +14,6 @@ import org.elasticsearch.common.settings.ImmutableSettings;
|
|||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.ScriptService.ScriptType;
|
||||
import org.elasticsearch.search.internal.InternalSearchResponse;
|
||||
|
@ -113,43 +111,6 @@ public class ScriptConditionTests extends ElasticsearchTestCase {
|
|||
fail("expected a condition exception trying to parse an invalid condition XContent");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testScriptResultParser_Valid() throws Exception {
|
||||
ScriptConditionFactory conditionParser = new ScriptConditionFactory(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp));
|
||||
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", true);
|
||||
builder.endObject();
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
ScriptCondition.Result scriptResult = conditionParser.parseResult("_id", parser);
|
||||
assertTrue(scriptResult.met());
|
||||
|
||||
builder = jsonBuilder();
|
||||
builder.startObject();
|
||||
builder.field("met", false);
|
||||
builder.endObject();
|
||||
|
||||
parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
scriptResult = conditionParser.parseResult("_id", parser);
|
||||
assertFalse(scriptResult.met());
|
||||
}
|
||||
|
||||
@Test(expected = ScriptConditionException.class)
|
||||
public void testScriptResultParser_Invalid() throws Exception {
|
||||
ScriptConditionFactory conditionParser = new ScriptConditionFactory(ImmutableSettings.settingsBuilder().build(), getScriptServiceProxy(tp));
|
||||
|
||||
XContentBuilder builder = jsonBuilder();
|
||||
builder.startObject().endObject();
|
||||
|
||||
conditionParser.parseResult("_id", XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()));
|
||||
fail("expected a condition exception trying to parse an invalid condition XContent");
|
||||
}
|
||||
|
||||
@Test(expected = ScriptConditionValidationException.class)
|
||||
@Repeat(iterations = 3)
|
||||
public void testScriptConditionParser_badScript() throws Exception {
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.watcher.condition.ExecutableCondition;
|
|||
import org.elasticsearch.watcher.condition.always.AlwaysCondition;
|
||||
import org.elasticsearch.watcher.condition.never.NeverCondition;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.input.ExecutableInput;
|
||||
import org.elasticsearch.watcher.input.Input;
|
||||
import org.elasticsearch.watcher.support.clock.Clock;
|
||||
|
@ -56,12 +57,13 @@ public class ExecutionServiceTests extends ElasticsearchTestCase {
|
|||
when(input.execute(any(WatchExecutionContext.class))).thenReturn(inputResult);
|
||||
|
||||
HistoryStore historyStore = mock(HistoryStore.class);
|
||||
TriggeredWatchStore triggeredWatchStore = mock(TriggeredWatchStore.class);
|
||||
WatchExecutor executor = mock(WatchExecutor.class);
|
||||
WatchStore watchStore = mock(WatchStore.class);
|
||||
WatchLockService watchLockService = mock(WatchLockService.class);
|
||||
WatcherSettingsValidation settingsValidator = mock(WatcherSettingsValidation.class);
|
||||
Clock clock = new ClockMock();
|
||||
executionService = new ExecutionService(ImmutableSettings.EMPTY, historyStore, executor, watchStore, watchLockService, clock, settingsValidator);
|
||||
executionService = new ExecutionService(ImmutableSettings.EMPTY, historyStore, triggeredWatchStore, executor, watchStore, watchLockService, clock, settingsValidator);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -112,10 +114,10 @@ public class ExecutionServiceTests extends ElasticsearchTestCase {
|
|||
when(watch.actions()).thenReturn(actions);
|
||||
when(watch.status()).thenReturn(watchStatus);
|
||||
|
||||
WatchExecutionResult executionResult = executionService.executeInner(context);
|
||||
assertThat(executionResult.conditionResult(), sameInstance(conditionResult));
|
||||
assertThat(executionResult.transformResult(), sameInstance(watchTransformResult));
|
||||
ActionWrapper.Result result = executionResult.actionsResults().get("_action");
|
||||
WatchRecord watchRecord = executionService.executeInner(context);
|
||||
assertThat(watchRecord.execution().conditionResult(), sameInstance(conditionResult));
|
||||
assertThat(watchRecord.execution().transformResult(), sameInstance(watchTransformResult));
|
||||
ActionWrapper.Result result = watchRecord.execution().actionsResults().get("_action");
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.id(), is("_action"));
|
||||
assertThat(result.transform(), sameInstance(actionTransformResult));
|
||||
|
@ -157,12 +159,12 @@ public class ExecutionServiceTests extends ElasticsearchTestCase {
|
|||
when(watch.actions()).thenReturn(actions);
|
||||
when(watch.status()).thenReturn(watchStatus);
|
||||
|
||||
WatchExecutionResult executionResult = executionService.executeInner(context);
|
||||
assertThat(executionResult.inputResult(), sameInstance(inputResult));
|
||||
assertThat(executionResult.conditionResult(), sameInstance(conditionResult));
|
||||
assertThat(executionResult.transformResult(), nullValue());
|
||||
assertThat(executionResult.actionsResults().count(), is(1));
|
||||
ActionWrapper.Result result = executionResult.actionsResults().get("_action");
|
||||
WatchRecord executionResult = executionService.executeInner(context);
|
||||
assertThat(executionResult.execution().inputResult(), sameInstance(inputResult));
|
||||
assertThat(executionResult.execution().conditionResult(), sameInstance(conditionResult));
|
||||
assertThat(executionResult.execution().transformResult(), nullValue());
|
||||
assertThat(executionResult.execution().actionsResults().count(), is(1));
|
||||
ActionWrapper.Result result = executionResult.execution().actionsResults().get("_action");
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.id(), is("_action"));
|
||||
assertThat(result.transform(), nullValue());
|
||||
|
@ -204,11 +206,11 @@ public class ExecutionServiceTests extends ElasticsearchTestCase {
|
|||
when(watch.actions()).thenReturn(actions);
|
||||
when(watch.status()).thenReturn(watchStatus);
|
||||
|
||||
WatchExecutionResult executionResult = executionService.executeInner(context);
|
||||
assertThat(executionResult.inputResult(), sameInstance(inputResult));
|
||||
assertThat(executionResult.conditionResult(), sameInstance(conditionResult));
|
||||
assertThat(executionResult.transformResult(), nullValue());
|
||||
assertThat(executionResult.actionsResults().count(), is(0));
|
||||
WatchRecord executionResult = executionService.executeInner(context);
|
||||
assertThat(executionResult.execution().inputResult(), sameInstance(inputResult));
|
||||
assertThat(executionResult.execution().conditionResult(), sameInstance(conditionResult));
|
||||
assertThat(executionResult.execution().transformResult(), nullValue());
|
||||
assertThat(executionResult.execution().actionsResults().count(), is(0));
|
||||
|
||||
verify(condition, times(1)).execute(context);
|
||||
verify(watchTransform, never()).execute(context, payload);
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.apache.lucene.util.LuceneTestCase.Slow;
|
|||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.index.engine.VersionConflictEngineException;
|
||||
import org.elasticsearch.index.engine.DocumentMissingException;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.WatcherService;
|
||||
import org.elasticsearch.watcher.actions.ActionStatus;
|
||||
|
@ -21,9 +21,9 @@ import org.elasticsearch.watcher.history.HistoryStore;
|
|||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.watcher.support.Script;
|
||||
import org.elasticsearch.watcher.support.xcontent.MapPath;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.delete.DeleteWatchResponse;
|
||||
import org.elasticsearch.watcher.transport.actions.execute.ExecuteWatchResponse;
|
||||
import org.elasticsearch.watcher.transport.actions.get.GetWatchRequest;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchRequest;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
|
@ -188,7 +188,6 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTests {
|
|||
@Test
|
||||
public void testExecutionRequestDefaults() throws Exception {
|
||||
ensureWatcherStarted();
|
||||
WatchRecord.Parser watchRecordParser = internalTestCluster().getInstance(WatchRecord.Parser.class);
|
||||
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
|
@ -203,15 +202,14 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTests {
|
|||
Wid wid = new Wid("_watchId",1,new DateTime());
|
||||
|
||||
|
||||
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch()
|
||||
Map<String, Object> executeWatchResult = watcherClient().prepareExecuteWatch()
|
||||
.setId("_id")
|
||||
.setTriggerEvent(triggerEvent)
|
||||
.get();
|
||||
.get().getRecordSource().getAsMap();
|
||||
|
||||
WatchRecord watchRecord = watchRecordParser.parse(wid.value(), 1, executeWatchResponse.getRecordSource().getBytes());
|
||||
|
||||
assertThat(watchRecord.state(), equalTo(WatchRecord.State.EXECUTION_NOT_NEEDED));
|
||||
assertThat(watchRecord.execution().inputResult().payload().data().get("foo").toString(), equalTo("bar"));
|
||||
assertThat(MapPath.<String>eval("state", executeWatchResult), equalTo(ExecutionState.EXECUTION_NOT_NEEDED.toString()));
|
||||
assertThat(MapPath.<String>eval("execution_result.input.simple.payload.foo", executeWatchResult), equalTo("bar"));
|
||||
|
||||
watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
|
@ -222,19 +220,20 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTests {
|
|||
watcherClient().putWatch(new PutWatchRequest("_id", watchBuilder)).actionGet();
|
||||
|
||||
|
||||
executeWatchResponse = watcherClient().prepareExecuteWatch().setId("_id").setTriggerEvent(triggerEvent).setRecordExecution(true).get();
|
||||
watchRecord = watchRecordParser.parse(wid.value(), 1, executeWatchResponse.getRecordSource().getBytes());
|
||||
executeWatchResult = watcherClient().prepareExecuteWatch()
|
||||
.setId("_id").setTriggerEvent(triggerEvent).setRecordExecution(true)
|
||||
.get().getRecordSource().getAsMap();
|
||||
|
||||
assertThat(MapPath.<String>eval("state", executeWatchResult), equalTo(ExecutionState.EXECUTED.toString()));
|
||||
assertThat(MapPath.<String>eval("execution_result.input.simple.payload.foo", executeWatchResult), equalTo("bar"));
|
||||
assertThat(MapPath.<String>eval("execution_result.actions.0.id", executeWatchResult), equalTo("log"));
|
||||
|
||||
|
||||
assertThat(watchRecord.state(), equalTo(WatchRecord.State.EXECUTED));
|
||||
assertThat(watchRecord.execution().inputResult().payload().data().get("foo").toString(), equalTo("bar"));
|
||||
assertThat(watchRecord.execution().actionsResults().get("log"), not(instanceOf(LoggingAction.Result.Simulated.class)));
|
||||
executeWatchResult = watcherClient().prepareExecuteWatch()
|
||||
.setId("_id").setTriggerEvent(triggerEvent)
|
||||
.get().getRecordSource().getAsMap();
|
||||
|
||||
|
||||
executeWatchResponse = watcherClient().prepareExecuteWatch().setId("_id").setTriggerEvent(triggerEvent).get();
|
||||
|
||||
watchRecord = watchRecordParser.parse(wid.value(), 1, executeWatchResponse.getRecordSource().getBytes());
|
||||
assertThat(watchRecord.state(), equalTo(WatchRecord.State.THROTTLED));
|
||||
assertThat(MapPath.<String>eval("state", executeWatchResult), equalTo(ExecutionState.THROTTLED.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -320,8 +319,8 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTests {
|
|||
startLatch.await();
|
||||
executionService.execute(ctxBuilder.build());
|
||||
fail("Execution of a deleted watch should fail but didn't");
|
||||
} catch (WatcherException we) {
|
||||
assertThat(we.getCause(), instanceOf(VersionConflictEngineException.class));
|
||||
} catch (WatchMissingException we) {
|
||||
assertThat(we.getCause(), instanceOf(DocumentMissingException.class));
|
||||
} catch (Throwable t) {
|
||||
throw new WatcherException("Failure mode execution of [{}] failed in an unexpected way", t, watchId);
|
||||
}
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
* 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.watcher.history;
|
||||
package org.elasticsearch.watcher.execution;
|
||||
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.watcher.condition.ExecutableCondition;
|
||||
import org.elasticsearch.watcher.condition.always.ExecutableAlwaysCondition;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.input.none.ExecutableNoneInput;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
|
@ -24,23 +23,23 @@ import static org.hamcrest.Matchers.*;
|
|||
|
||||
/**
|
||||
*/
|
||||
public class HistoryStoreLifeCycleTest extends AbstractWatcherIntegrationTests {
|
||||
public class TriggeredWatchStoreLifeCycleTest extends AbstractWatcherIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testPutLoadUpdate() throws Exception {
|
||||
ExecutableCondition condition = new ExecutableAlwaysCondition(logger);
|
||||
HistoryStore historyStore = getInstanceFromMaster(HistoryStore.class);
|
||||
TriggeredWatchStore triggeredWatchStore = getInstanceFromMaster(TriggeredWatchStore.class);
|
||||
Watch watch = new Watch("_name", null, new ExecutableNoneInput(logger), condition, null, null, null, null, null);
|
||||
|
||||
// Put watch records and verify that these are stored
|
||||
WatchRecord[] watchRecords = new WatchRecord[randomIntBetween(1, 50)];
|
||||
for (int i = 0; i < watchRecords.length; i++) {
|
||||
TriggeredWatch[] triggeredWatches = new TriggeredWatch[randomIntBetween(1, 50)];
|
||||
for (int i = 0; i < triggeredWatches.length; i++) {
|
||||
DateTime dateTime = new DateTime(i, UTC);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), dateTime, dateTime);
|
||||
Wid wid = new Wid("record_" + i, randomLong(), DateTime.now(UTC));
|
||||
watchRecords[i] = new WatchRecord(wid, watch, event);
|
||||
historyStore.put(watchRecords[i]);
|
||||
GetResponse getResponse = client().prepareGet(HistoryStore.getHistoryIndexNameForTime(dateTime), HistoryStore.DOC_TYPE, watchRecords[i].id().value())
|
||||
triggeredWatches[i] = new TriggeredWatch(wid, event);
|
||||
triggeredWatchStore.put(triggeredWatches[i]);
|
||||
GetResponse getResponse = client().prepareGet(TriggeredWatchStore.INDEX_NAME, TriggeredWatchStore.DOC_TYPE, triggeredWatches[i].id().value())
|
||||
.setVersion(1)
|
||||
.get();
|
||||
assertThat(getResponse.isExists(), equalTo(true));
|
||||
|
@ -48,26 +47,24 @@ public class HistoryStoreLifeCycleTest extends AbstractWatcherIntegrationTests {
|
|||
|
||||
// Load the stored watch records
|
||||
ClusterService clusterService = getInstanceFromMaster(ClusterService.class);
|
||||
Collection<WatchRecord> records = historyStore.loadRecords(clusterService.state(), WatchRecord.State.AWAITS_EXECUTION);
|
||||
assertThat(records, notNullValue());
|
||||
assertThat(records, hasSize(watchRecords.length));
|
||||
Collection<TriggeredWatch> loadedTriggeredWatches = triggeredWatchStore.loadTriggeredWatches(clusterService.state());
|
||||
assertThat(loadedTriggeredWatches, notNullValue());
|
||||
assertThat(loadedTriggeredWatches, hasSize(triggeredWatches.length));
|
||||
|
||||
// Change the state to executed and update the watch records and then verify if the changes have been persisted too
|
||||
for (WatchRecord watchRecord : watchRecords) {
|
||||
assertThat(records.contains(watchRecord), is(true));
|
||||
assertThat(watchRecord.version(), equalTo(1l));
|
||||
watchRecord.update(WatchRecord.State.EXECUTED, "_message");
|
||||
historyStore.update(watchRecord);
|
||||
GetResponse getResponse = client().prepareGet(HistoryStore.getHistoryIndexNameForTime(watchRecord.triggerEvent().triggeredTime()), HistoryStore.DOC_TYPE, watchRecord.id().value())
|
||||
for (TriggeredWatch triggeredWatch : triggeredWatches) {
|
||||
assertThat(loadedTriggeredWatches.contains(triggeredWatch), is(true));
|
||||
triggeredWatchStore.delete(triggeredWatch.id());
|
||||
GetResponse getResponse = client().prepareGet(TriggeredWatchStore.INDEX_NAME, TriggeredWatchStore.DOC_TYPE, triggeredWatch.id().value())
|
||||
.setVersion(2l)
|
||||
.get();
|
||||
assertThat(getResponse.isExists(), equalTo(true));
|
||||
assertThat(getResponse.isExists(), equalTo(false));
|
||||
}
|
||||
|
||||
// try to load watch records, but none are in the await state, so no watch records are loaded.
|
||||
records = historyStore.loadRecords(clusterService.state(), WatchRecord.State.AWAITS_EXECUTION);
|
||||
assertThat(records, notNullValue());
|
||||
assertThat(records, hasSize(0));
|
||||
loadedTriggeredWatches = triggeredWatchStore.loadTriggeredWatches(clusterService.state());
|
||||
assertThat(loadedTriggeredWatches, notNullValue());
|
||||
assertThat(loadedTriggeredWatches, hasSize(0));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* 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.watcher.execution;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.search.ClearScrollResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.routing.*;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.text.StringText;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.search.SearchShardTarget;
|
||||
import org.elasticsearch.search.internal.InternalSearchHit;
|
||||
import org.elasticsearch.search.internal.InternalSearchHits;
|
||||
import org.elasticsearch.search.internal.InternalSearchResponse;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.watcher.execution.TriggeredWatch;
|
||||
import org.elasticsearch.watcher.execution.TriggeredWatchStore;
|
||||
import org.elasticsearch.watcher.history.TriggeredWatchException;
|
||||
import org.elasticsearch.watcher.support.TemplateUtils;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
import org.hamcrest.core.IsNull;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class TriggeredWatchStoreTests extends ElasticsearchTestCase {
|
||||
|
||||
private ClientProxy clientProxy;
|
||||
private TriggeredWatch.Parser parser;
|
||||
private TriggeredWatchStore triggeredWatchStore;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
clientProxy = mock(ClientProxy.class);
|
||||
TemplateUtils templateUtils = mock(TemplateUtils.class);
|
||||
parser = mock(TriggeredWatch.Parser.class);
|
||||
triggeredWatchStore = new TriggeredWatchStore(ImmutableSettings.EMPTY, clientProxy, templateUtils, parser);
|
||||
triggeredWatchStore.start();
|
||||
verify(templateUtils, times(1)).putTemplate(same(TriggeredWatchStore.INDEX_TEMPLATE_NAME), any(Settings.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_noPriorHistoryIndices() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("name"));
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(triggeredWatchStore.validate(cs), is(true));
|
||||
Collection<TriggeredWatch> records = triggeredWatchStore.loadTriggeredWatches(cs);
|
||||
assertThat(records, notNullValue());
|
||||
assertThat(records, hasSize(0));
|
||||
verifyZeroInteractions(clientProxy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_noActivePrimaryShards() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
|
||||
String indexName = TriggeredWatchStore.INDEX_NAME;
|
||||
int numShards = 2 + randomInt(2);
|
||||
int numStartedShards = 1;
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, numShards)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(numShards).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
for (int i = 0; i < numShards; i++) {
|
||||
ShardRoutingState state;
|
||||
if (numStartedShards-- > 0) {
|
||||
state = ShardRoutingState.STARTED;
|
||||
} else {
|
||||
state = ShardRoutingState.UNASSIGNED;
|
||||
}
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_id", null, true, state, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
}
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(triggeredWatchStore.validate(cs), is(false));
|
||||
try {
|
||||
triggeredWatchStore.loadTriggeredWatches(cs);
|
||||
fail("exception expected, because not all primary shards are started");
|
||||
} catch (TriggeredWatchException e) {
|
||||
assertThat(e.getMessage(), equalTo("not all primary shards of the [.triggered_watches] index are started."));
|
||||
}
|
||||
|
||||
verifyZeroInteractions(clientProxy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_refreshNotHittingAllShards() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = TriggeredWatchStore.INDEX_NAME;
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_id", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(triggeredWatchStore.validate(cs), is(true));
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 0);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
try {
|
||||
triggeredWatchStore.loadTriggeredWatches(cs);
|
||||
fail("exception expected, because refresh did't manage to run on all primary shards");
|
||||
} catch (TriggeredWatchException e) {
|
||||
assertThat(e.getMessage(), equalTo("refresh was supposed to run on [1] shards, but ran on [0] shards"));
|
||||
}
|
||||
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_searchNotHittingAllShards() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = TriggeredWatchStore.INDEX_NAME;
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_name", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse = mock(SearchResponse.class);
|
||||
when(searchResponse.getSuccessfulShards()).thenReturn(0);
|
||||
when(searchResponse.getTotalShards()).thenReturn(1);
|
||||
when(clientProxy.search(any(SearchRequest.class))).thenReturn(searchResponse);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 1));
|
||||
|
||||
assertThat(triggeredWatchStore.validate(cs), is(true));
|
||||
try {
|
||||
triggeredWatchStore.loadTriggeredWatches(cs);
|
||||
fail("exception expected, because scan search didn't manage to run on all shards");
|
||||
} catch (TriggeredWatchException e) {
|
||||
assertThat(e.getMessage(), equalTo("scan search was supposed to run on [1] shards, but ran on [0] shards"));
|
||||
}
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, times(1)).search(any(SearchRequest.class));
|
||||
verify(clientProxy, times(1)).clearScroll(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_noHistoryEntries() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = TriggeredWatchStore.INDEX_NAME;
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_name", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse = mock(SearchResponse.class);
|
||||
when(searchResponse.getSuccessfulShards()).thenReturn(1);
|
||||
when(searchResponse.getTotalShards()).thenReturn(1);
|
||||
when(searchResponse.getHits()).thenReturn(InternalSearchHits.empty());
|
||||
when(clientProxy.search(any(SearchRequest.class))).thenReturn(searchResponse);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 1));
|
||||
|
||||
assertThat(triggeredWatchStore.validate(cs), is(true));
|
||||
Collection<TriggeredWatch> triggeredWatches = triggeredWatchStore.loadTriggeredWatches(cs);
|
||||
assertThat(triggeredWatches, IsNull.notNullValue());
|
||||
assertThat(triggeredWatches, hasSize(0));
|
||||
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, times(1)).search(any(SearchRequest.class));
|
||||
verify(clientProxy, times(1)).clearScroll(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_foundHistoryEntries() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = TriggeredWatchStore.INDEX_NAME;
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_id", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse1 = mock(SearchResponse.class);
|
||||
when(searchResponse1.getSuccessfulShards()).thenReturn(1);
|
||||
when(searchResponse1.getTotalShards()).thenReturn(1);
|
||||
InternalSearchHit hit = new InternalSearchHit(0, "_id", new StringText("_type"), null);
|
||||
hit.version(1l);
|
||||
hit.shard(new SearchShardTarget("_node_id", indexName, 0));
|
||||
hit.sourceRef(new BytesArray("{}"));
|
||||
InternalSearchHits hits = new InternalSearchHits(new InternalSearchHit[]{hit}, 1, 1.0f);
|
||||
when(searchResponse1.getHits()).thenReturn(hits);
|
||||
when(searchResponse1.getScrollId()).thenReturn("_scrollId");
|
||||
when(clientProxy.search(any(SearchRequest.class))).thenReturn(searchResponse1);
|
||||
|
||||
// First return a scroll response with a single hit and then with no hits
|
||||
SearchResponse searchResponse2 = new SearchResponse(InternalSearchResponse.empty(), "_scrollId", 1, 1, 1, null);
|
||||
when(clientProxy.searchScroll(eq("_scrollId"), any(TimeValue.class))).thenReturn(searchResponse1).thenReturn(searchResponse2);
|
||||
|
||||
|
||||
TriggeredWatch triggeredWatch = mock(TriggeredWatch.class);
|
||||
when(parser.parse(eq("_id"), eq(1l), any(BytesReference.class))).thenReturn(triggeredWatch);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 1));
|
||||
|
||||
assertThat(triggeredWatchStore.validate(cs), is(true));
|
||||
Collection<TriggeredWatch> triggeredWatches = triggeredWatchStore.loadTriggeredWatches(cs);
|
||||
assertThat(triggeredWatches, notNullValue());
|
||||
assertThat(triggeredWatches, hasSize(1));
|
||||
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
verify(clientProxy, times(1)).search(any(SearchRequest.class));
|
||||
verify(clientProxy, times(2)).searchScroll(anyString(), any(TimeValue.class));
|
||||
verify(clientProxy, times(1)).clearScroll(anyString());
|
||||
}
|
||||
|
||||
private RefreshResponse mockRefreshResponse(int total, int successful) {
|
||||
RefreshResponse refreshResponse = mock(RefreshResponse.class);
|
||||
when(refreshResponse.getTotalShards()).thenReturn(total);
|
||||
when(refreshResponse.getSuccessfulShards()).thenReturn(successful);
|
||||
return refreshResponse;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.execution;
|
||||
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.watcher.execution.*;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.test.WatcherTestUtils;
|
||||
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.watcher.watch.Watch;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class TriggeredWatchTests extends AbstractWatcherIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testParser() throws Exception {
|
||||
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), watcherHttpClient(), noopEmailService(), logger);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), DateTime.now(UTC), DateTime.now(UTC));
|
||||
Wid wid = new Wid("_record", randomLong(), DateTime.now(UTC));
|
||||
TriggeredWatch triggeredWatch = new TriggeredWatch(wid, event);
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
triggeredWatch.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
TriggeredWatch parsedTriggeredWatch = triggeredWatchParser().parse(triggeredWatch.id().value(), 0, jsonBuilder.bytes());
|
||||
|
||||
XContentBuilder jsonBuilder2 = XContentFactory.jsonBuilder();
|
||||
parsedTriggeredWatch.toXContent(jsonBuilder2, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
assertThat(jsonBuilder.bytes().toUtf8(), equalTo(jsonBuilder2.bytes().toUtf8()));
|
||||
}
|
||||
|
||||
private TriggeredWatch.Parser triggeredWatchParser() {
|
||||
return internalTestCluster().getInstance(TriggeredWatch.Parser.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -5,53 +5,26 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.history;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.ClearScrollResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.routing.*;
|
||||
import org.elasticsearch.cluster.settings.DynamicSettings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.text.StringText;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.search.SearchShardTarget;
|
||||
import org.elasticsearch.search.internal.InternalSearchHit;
|
||||
import org.elasticsearch.search.internal.InternalSearchHits;
|
||||
import org.elasticsearch.search.internal.InternalSearchResponse;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.condition.always.ExecutableAlwaysCondition;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.input.none.ExecutableNoneInput;
|
||||
import org.elasticsearch.watcher.support.TemplateUtils;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.watcher.watch.Watch;
|
||||
import org.hamcrest.core.IsNull;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.mockito.Matchers;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
import static org.elasticsearch.watcher.test.WatcherMatchers.indexRequest;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
|
@ -60,75 +33,36 @@ public class HistoryStoreTests extends ElasticsearchTestCase {
|
|||
|
||||
private HistoryStore historyStore;
|
||||
private ClientProxy clientProxy;
|
||||
private TemplateUtils templateUtils;
|
||||
private WatchRecord.Parser parser;
|
||||
private NodeSettingsService nodeSettingsService;
|
||||
private DynamicSettings dynamicSettings;
|
||||
private ThreadPool threadPool;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
clientProxy = mock(ClientProxy.class);
|
||||
templateUtils = mock(TemplateUtils.class);
|
||||
parser = mock(WatchRecord.Parser.class);
|
||||
nodeSettingsService = mock(NodeSettingsService.class);
|
||||
dynamicSettings = mock(DynamicSettings.class);
|
||||
threadPool = mock(ThreadPool.class);
|
||||
historyStore = new HistoryStore(ImmutableSettings.EMPTY, clientProxy, templateUtils, parser, nodeSettingsService, dynamicSettings, threadPool);
|
||||
TemplateUtils templateUtils = mock(TemplateUtils.class);
|
||||
NodeSettingsService nodeSettingsService = mock(NodeSettingsService.class);
|
||||
DynamicSettings dynamicSettings = mock(DynamicSettings.class);
|
||||
ThreadPool threadPool = mock(ThreadPool.class);
|
||||
historyStore = new HistoryStore(ImmutableSettings.EMPTY, clientProxy, templateUtils, nodeSettingsService, dynamicSettings, threadPool);
|
||||
historyStore.start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPut() throws Exception {
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_name");
|
||||
when(watch.condition()).thenReturn(new ExecutableAlwaysCondition(logger));
|
||||
when(watch.input()).thenReturn(new ExecutableNoneInput(logger));
|
||||
when(watch.metadata()).thenReturn(null);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), new DateTime(0, UTC), new DateTime(0, UTC));
|
||||
Wid wid = new Wid("_name", 0, new DateTime(0, UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watch, event);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(wid.watchId(), new DateTime(0, UTC), new DateTime(0, UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, event, null, ExecutionState.EXECUTED);
|
||||
|
||||
IndexResponse indexResponse = mock(IndexResponse.class);
|
||||
long version = randomLong();
|
||||
when(indexResponse.getVersion()).thenReturn(version);
|
||||
|
||||
when(clientProxy.index(indexRequest(".watch_history-1970.01.01", HistoryStore.DOC_TYPE, wid.value(), IndexRequest.OpType.CREATE))).thenReturn(indexResponse);
|
||||
IndexRequest indexRequest = indexRequest(".watch_history-1970.01.01", HistoryStore.DOC_TYPE, wid.value(), IndexRequest.OpType.CREATE);
|
||||
when(clientProxy.index(indexRequest)).thenReturn(indexResponse);
|
||||
historyStore.put(watchRecord);
|
||||
assertThat(watchRecord.version(), equalTo(version));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws Exception {
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_name");
|
||||
when(watch.condition()).thenReturn(new ExecutableAlwaysCondition(logger));
|
||||
when(watch.input()).thenReturn(new ExecutableNoneInput(logger));
|
||||
when(watch.metadata()).thenReturn(null);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), new DateTime(0, UTC), new DateTime(0, UTC));
|
||||
Wid wid = new Wid("_name", 0, new DateTime(0, UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watch, event);
|
||||
watchRecord.version(4l);
|
||||
|
||||
IndexResponse indexResponse = mock(IndexResponse.class);
|
||||
long version = randomLong();
|
||||
when(indexResponse.getVersion()).thenReturn(version);
|
||||
|
||||
when(clientProxy.index(indexRequest(".watch_history-1970.01.01", HistoryStore.DOC_TYPE, wid.value(), 4L, null))).thenReturn(indexResponse);
|
||||
historyStore.update(watchRecord);
|
||||
assertThat(watchRecord.version(), equalTo(version));
|
||||
verify(clientProxy).index(Matchers.<IndexRequest>any());
|
||||
}
|
||||
|
||||
@Test(expected = HistoryException.class)
|
||||
public void testPut_stopped() {
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_name");
|
||||
when(watch.condition()).thenReturn(new ExecutableAlwaysCondition(logger));
|
||||
when(watch.input()).thenReturn(null);
|
||||
when(watch.metadata()).thenReturn(null);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), new DateTime(0, UTC), new DateTime(0, UTC));
|
||||
Wid wid = new Wid("_name", 0, new DateTime(0, UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watch, event);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(wid.watchId(), new DateTime(0, UTC), new DateTime(0, UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, event, null, ExecutionState.EXECUTED);
|
||||
|
||||
historyStore.stop();
|
||||
try {
|
||||
|
@ -139,264 +73,6 @@ public class HistoryStoreTests extends ElasticsearchTestCase {
|
|||
fail();
|
||||
}
|
||||
|
||||
@Test(expected = HistoryException.class)
|
||||
public void testUpdate_stopped() throws Exception {
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_name");
|
||||
when(watch.condition()).thenReturn(new ExecutableAlwaysCondition(logger));
|
||||
when(watch.input()).thenReturn(null);
|
||||
when(watch.metadata()).thenReturn(null);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), new DateTime(0, UTC), new DateTime(0, UTC));
|
||||
Wid wid = new Wid("_name", 0, new DateTime(0, UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watch, event);
|
||||
|
||||
historyStore.stop();
|
||||
try {
|
||||
historyStore.update(watchRecord);
|
||||
} finally {
|
||||
historyStore.start();
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_noPriorHistoryIndices() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("name"));
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(historyStore.validate(cs), is(true));
|
||||
Collection<WatchRecord> records = historyStore.loadRecords(cs, WatchRecord.State.AWAITS_EXECUTION);
|
||||
assertThat(records, notNullValue());
|
||||
assertThat(records, hasSize(0));
|
||||
verify(templateUtils, times(1)).putTemplate(same("watch_history"), any(Settings.class));
|
||||
verifyZeroInteractions(clientProxy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_noActivePrimaryShards() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
int numIndices = randomIntBetween(2, 10);
|
||||
int numStartedShards = randomIntBetween(1, numIndices - 1);
|
||||
for (int i = 0; i < numIndices; i++) {
|
||||
String indexName = HistoryStore.INDEX_PREFIX + i;
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
ShardRoutingState state;
|
||||
if (numStartedShards-- > 0) {
|
||||
state = ShardRoutingState.STARTED;
|
||||
} else {
|
||||
state = ShardRoutingState.UNASSIGNED;
|
||||
}
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_id", null, true, state, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
}
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(historyStore.validate(cs), is(false));
|
||||
try {
|
||||
historyStore.loadRecords(cs, WatchRecord.State.AWAITS_EXECUTION);
|
||||
fail("exception expected, because not all primary shards are started");
|
||||
} catch (HistoryException e) {
|
||||
assertThat(e.getMessage(), containsString("not all primary shards of the [.watch_history-"));
|
||||
}
|
||||
|
||||
verifyZeroInteractions(templateUtils);
|
||||
verifyZeroInteractions(clientProxy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_refreshNotHittingAllShards() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = HistoryStore.INDEX_PREFIX + "1";
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_id", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
assertThat(historyStore.validate(cs), is(true));
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 0);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
try {
|
||||
historyStore.loadRecords(cs, WatchRecord.State.AWAITS_EXECUTION);
|
||||
fail("exception expected, because refresh did't manage to run on all primary shards");
|
||||
} catch (HistoryException e) {
|
||||
assertThat(e.getMessage(), equalTo("refresh was supposed to run on [1] shards, but ran on [0] shards"));
|
||||
}
|
||||
|
||||
verifyZeroInteractions(templateUtils);
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_searchNotHittingAllShards() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = HistoryStore.INDEX_PREFIX + "1";
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_name", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse = mock(SearchResponse.class);
|
||||
when(searchResponse.getSuccessfulShards()).thenReturn(0);
|
||||
when(searchResponse.getTotalShards()).thenReturn(1);
|
||||
when(clientProxy.search(any(SearchRequest.class))).thenReturn(searchResponse);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 1));
|
||||
|
||||
assertThat(historyStore.validate(cs), is(true));
|
||||
try {
|
||||
historyStore.loadRecords(cs, WatchRecord.State.AWAITS_EXECUTION);
|
||||
fail("exception expected, because scan search didn't manage to run on all shards");
|
||||
} catch (HistoryException e) {
|
||||
assertThat(e.getMessage(), equalTo("scan search was supposed to run on [1] shards, but ran on [0] shards"));
|
||||
}
|
||||
verifyZeroInteractions(templateUtils);
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_noHistoryEntries() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = HistoryStore.INDEX_PREFIX + "1";
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_name", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse = mock(SearchResponse.class);
|
||||
when(searchResponse.getSuccessfulShards()).thenReturn(1);
|
||||
when(searchResponse.getTotalShards()).thenReturn(1);
|
||||
when(searchResponse.getHits()).thenReturn(InternalSearchHits.empty());
|
||||
when(clientProxy.search(any(SearchRequest.class))).thenReturn(searchResponse);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 1));
|
||||
|
||||
assertThat(historyStore.validate(cs), is(true));
|
||||
Collection<WatchRecord> records = historyStore.loadRecords(cs, WatchRecord.State.AWAITS_EXECUTION);
|
||||
assertThat(records, IsNull.notNullValue());
|
||||
assertThat(records, hasSize(0));
|
||||
|
||||
verify(templateUtils, times(1)).putTemplate(same("watch_history"), any(Settings.class));
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadWatchRecords_foundHistoryEntries() throws Exception {
|
||||
ClusterState.Builder csBuilder = new ClusterState.Builder(new ClusterName("_name"));
|
||||
|
||||
RoutingTable.Builder routingTableBuilder = RoutingTable.builder();
|
||||
MetaData.Builder metaDateBuilder = MetaData.builder();
|
||||
String indexName = HistoryStore.INDEX_PREFIX + "1";
|
||||
Settings settings = ImmutableSettings.builder()
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1)
|
||||
.build();
|
||||
metaDateBuilder.put(IndexMetaData.builder(indexName).settings(settings).numberOfShards(1).numberOfReplicas(1));
|
||||
IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(indexName);
|
||||
indexRoutingTableBuilder.addIndexShard(new IndexShardRoutingTable.Builder(new ShardId(indexName, 0), false)
|
||||
.addShard(new ImmutableShardRouting(indexName, 0, "_node_id", null, true, ShardRoutingState.STARTED, 1))
|
||||
.build());
|
||||
indexRoutingTableBuilder.addReplica();
|
||||
routingTableBuilder.add(indexRoutingTableBuilder.build());
|
||||
csBuilder.metaData(metaDateBuilder);
|
||||
csBuilder.routingTable(routingTableBuilder);
|
||||
ClusterState cs = csBuilder.build();
|
||||
|
||||
RefreshResponse refreshResponse = mockRefreshResponse(1, 1);
|
||||
when(clientProxy.refresh(any(RefreshRequest.class))).thenReturn(refreshResponse);
|
||||
|
||||
SearchResponse searchResponse1 = mock(SearchResponse.class);
|
||||
when(searchResponse1.getSuccessfulShards()).thenReturn(1);
|
||||
when(searchResponse1.getTotalShards()).thenReturn(1);
|
||||
InternalSearchHit hit = new InternalSearchHit(0, "_id", new StringText("_type"), null);
|
||||
hit.version(1l);
|
||||
hit.shard(new SearchShardTarget("_node_id", indexName, 0));
|
||||
hit.sourceRef(new BytesArray("{}"));
|
||||
InternalSearchHits hits = new InternalSearchHits(new InternalSearchHit[]{hit}, 1, 1.0f);
|
||||
when(searchResponse1.getHits()).thenReturn(hits);
|
||||
when(clientProxy.search(any(SearchRequest.class))).thenReturn(searchResponse1);
|
||||
|
||||
// First return a scroll response with a single hit
|
||||
when(clientProxy.searchScroll(anyString(), any(TimeValue.class))).thenReturn(searchResponse1);
|
||||
|
||||
// then with no hits
|
||||
SearchResponse searchResponse2 = new SearchResponse(InternalSearchResponse.empty(), null, 1, 1, 1, null);
|
||||
when(clientProxy.searchScroll(anyString(), any(TimeValue.class))).thenReturn(searchResponse2);
|
||||
|
||||
WatchRecord watchRecord = mock(WatchRecord.class);
|
||||
when(watchRecord.state()).thenReturn(WatchRecord.State.AWAITS_EXECUTION);
|
||||
when(parser.parse(eq("_id"), eq(1l), any(BytesReference.class))).thenReturn(watchRecord);
|
||||
|
||||
when(clientProxy.clearScroll(anyString())).thenReturn(new ClearScrollResponse(true, 1));
|
||||
|
||||
assertThat(historyStore.validate(cs), is(true));
|
||||
Collection<WatchRecord> records = historyStore.loadRecords(cs, WatchRecord.State.AWAITS_EXECUTION);
|
||||
assertThat(records, notNullValue());
|
||||
assertThat(records, hasSize(0));
|
||||
|
||||
verify(templateUtils, times(1)).putTemplate(same("watch_history"), any(Settings.class));
|
||||
verify(clientProxy, times(1)).refresh(any(RefreshRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexNameGeneration() {
|
||||
assertThat(HistoryStore.getHistoryIndexNameForTime(new DateTime(0, UTC)), equalTo(".watch_history-1970.01.01"));
|
||||
|
@ -405,11 +81,4 @@ public class HistoryStoreTests extends ElasticsearchTestCase {
|
|||
assertThat(HistoryStore.getHistoryIndexNameForTime(new DateTime(2833165811000L, UTC)), equalTo(".watch_history-2059.10.12"));
|
||||
}
|
||||
|
||||
private RefreshResponse mockRefreshResponse(int total, int successful) {
|
||||
RefreshResponse refreshResponse = mock(RefreshResponse.class);
|
||||
when(refreshResponse.getTotalShards()).thenReturn(total);
|
||||
when(refreshResponse.getSuccessfulShards()).thenReturn(successful);
|
||||
return refreshResponse;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,116 +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.watcher.history;
|
||||
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.ActionWrapper;
|
||||
import org.elasticsearch.watcher.actions.email.EmailAction;
|
||||
import org.elasticsearch.watcher.actions.webhook.WebhookAction;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.condition.always.AlwaysCondition;
|
||||
import org.elasticsearch.watcher.execution.TriggeredExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionResult;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpResponse;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.test.WatcherTestUtils;
|
||||
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
import org.elasticsearch.watcher.watch.Watch;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class WatchRecordTests extends AbstractWatcherIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testParser() throws Exception {
|
||||
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), watcherHttpClient(), noopEmailService(), logger);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), DateTime.now(UTC), DateTime.now(UTC));
|
||||
Wid wid = new Wid("_record", randomLong(), DateTime.now(UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watch, event);
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
watchRecord.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
WatchRecord parsedWatchRecord = watchRecordParser().parse(watchRecord.id().value(), 0, jsonBuilder.bytes());
|
||||
|
||||
XContentBuilder jsonBuilder2 = XContentFactory.jsonBuilder();
|
||||
parsedWatchRecord.toXContent(jsonBuilder2, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
assertThat(jsonBuilder.bytes().toUtf8(), equalTo(jsonBuilder2.bytes().toUtf8()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_WithSealedWatchRecord() throws Exception {
|
||||
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), watcherHttpClient(), noopEmailService(), logger);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), DateTime.now(UTC), DateTime.now(UTC));
|
||||
Wid wid = new Wid("_record", randomLong(), DateTime.now(UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watch, event);
|
||||
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, new DateTime(), event, timeValueSeconds(5));
|
||||
ctx.onActionResult(new ActionWrapper.Result("_email", new Action.Result.Failure(EmailAction.TYPE, "failed to send because blah")));
|
||||
HttpRequest request = HttpRequest.builder("localhost", 8000)
|
||||
.path("/watchfoo")
|
||||
.body("{'awesome' : 'us'}")
|
||||
.build();
|
||||
ctx.onActionResult(new ActionWrapper.Result("_webhook", new WebhookAction.Result.Success(request, new HttpResponse(300))));
|
||||
SimpleInput.Result inputResult = new SimpleInput.Result(new Payload.Simple());
|
||||
Condition.Result conditionResult = AlwaysCondition.Result.INSTANCE;
|
||||
ctx.onInputResult(inputResult);
|
||||
ctx.onConditionResult(conditionResult);
|
||||
long watchExecutionDuration = randomIntBetween(30, 100000);
|
||||
watchRecord.seal(new WatchExecutionResult(ctx, watchExecutionDuration));
|
||||
assertThat(watchRecord.execution().executionDurationMs(), equalTo(watchExecutionDuration));
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
watchRecord.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
WatchRecord parsedWatchRecord = watchRecordParser().parse(watchRecord.id().value(), 0, jsonBuilder.bytes());
|
||||
|
||||
XContentBuilder jsonBuilder2 = XContentFactory.jsonBuilder();
|
||||
parsedWatchRecord.toXContent(jsonBuilder2, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
assertThat(jsonBuilder.bytes().toUtf8(), equalTo(jsonBuilder2.bytes().toUtf8()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser_WithSealedWatchRecord_WithScriptSearchCondition() throws Exception {
|
||||
Watch watch = WatcherTestUtils.createTestWatch("fired_test", scriptService(), watcherHttpClient(), noopEmailService(), logger);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watch.id(), DateTime.now(UTC), DateTime.now(UTC));
|
||||
WatchExecutionContext ctx = new TriggeredExecutionContext( watch, new DateTime(), event, timeValueSeconds(5));
|
||||
WatchRecord watchRecord = new WatchRecord(ctx.id(), watch, event);
|
||||
ctx.onActionResult(new ActionWrapper.Result("_email", new Action.Result.Failure(EmailAction.TYPE, "failed to send because blah")));
|
||||
HttpRequest request = HttpRequest.builder("localhost", 8000)
|
||||
.path("/watchfoo")
|
||||
.body("{'awesome' : 'us'}")
|
||||
.build();
|
||||
ctx.onActionResult(new ActionWrapper.Result("_webhook", new WebhookAction.Result.Success(request, new HttpResponse(300))));
|
||||
SimpleInput.Result inputResult = new SimpleInput.Result(new Payload.Simple());
|
||||
Condition.Result conditionResult = AlwaysCondition.Result.INSTANCE;
|
||||
ctx.onInputResult(inputResult);
|
||||
ctx.onConditionResult(conditionResult);
|
||||
long watchExecutionDuration = randomIntBetween(30, 100000);
|
||||
watchRecord.seal(new WatchExecutionResult(ctx, watchExecutionDuration));
|
||||
assertThat(watchRecord.execution().executionDurationMs(), equalTo(watchExecutionDuration));
|
||||
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
|
||||
watchRecord.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
WatchRecord parsedWatchRecord = watchRecordParser().parse(watchRecord.id().value(), 0, jsonBuilder.bytes());
|
||||
|
||||
XContentBuilder jsonBuilder2 = XContentFactory.jsonBuilder();
|
||||
parsedWatchRecord.toXContent(jsonBuilder2, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
assertThat(jsonBuilder.bytes().toUtf8(), equalTo(jsonBuilder2.bytes().toUtf8()));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -193,37 +193,4 @@ public class HttpInputTests extends ElasticsearchTestCase {
|
|||
httpParser.parseInput("_id", parser);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseResult() throws Exception {
|
||||
HttpMethod httpMethod = HttpMethod.GET;
|
||||
String body = "_body";
|
||||
Map<String, Template> headers = new MapBuilder<String, Template>().put("a", Template.inline("b").build()).map();
|
||||
HttpRequest request = HttpRequest.builder("_host", 123)
|
||||
.method(httpMethod)
|
||||
.body(body)
|
||||
.setHeader("a", "b")
|
||||
.build();
|
||||
|
||||
Map<String, Object> payload = MapBuilder.<String, Object>newMapBuilder().put("x", "y").map();
|
||||
|
||||
XContentBuilder builder = jsonBuilder().startObject();
|
||||
builder.field(HttpInput.Field.STATUS.getPreferredName(), 123);
|
||||
builder.field(HttpInput.Field.REQUEST.getPreferredName(), request);
|
||||
builder.field(HttpInput.Field.PAYLOAD.getPreferredName(), payload);
|
||||
builder.endObject();
|
||||
|
||||
XContentParser parser = XContentHelper.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
HttpInput.Result result = httpParser.parseResult("_id", parser);
|
||||
assertThat(result.type(), equalTo(HttpInput.TYPE));
|
||||
assertThat(result.payload().data(), equalTo(payload));
|
||||
assertThat(result.status(), equalTo(123));
|
||||
assertThat(result.request().method().method(), equalTo("GET"));
|
||||
assertThat(result.request().headers().size(), equalTo(headers.size()));
|
||||
assertThat(result.request().headers(), hasEntry("a", (Object) "b"));
|
||||
assertThat(result.request().host(), equalTo("_host"));
|
||||
assertThat(result.request().port(), equalTo(123));
|
||||
assertThat(result.request().body(), equalTo("_body"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import org.elasticsearch.common.collect.ImmutableMap;
|
|||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
|
@ -28,7 +27,6 @@ import org.elasticsearch.watcher.execution.TriggeredExecutionContext;
|
|||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.input.simple.ExecutableSimpleInput;
|
||||
import org.elasticsearch.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.watcher.support.WatcherUtils;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
import org.elasticsearch.watcher.trigger.schedule.IntervalSchedule;
|
||||
|
@ -42,7 +40,6 @@ import org.junit.Test;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
|
@ -57,8 +54,6 @@ import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.SUITE;
|
|||
import static org.elasticsearch.watcher.test.WatcherTestUtils.areJsonEquivalent;
|
||||
import static org.elasticsearch.watcher.test.WatcherTestUtils.getRandomSupportedSearchType;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
*/
|
||||
|
@ -248,59 +243,6 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
|
|||
fail("expected a SearchInputException as search type SCAN should not be supported");
|
||||
}
|
||||
|
||||
@Test(expected = SearchInputException.class)
|
||||
public void testParser_Invalid() throws Exception {
|
||||
SearchInputFactory factory = new SearchInputFactory(settingsBuilder().build(), ClientProxy.of(client()));
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>());
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(SearchInput.Field.PAYLOAD.getPreferredName(), data);
|
||||
jsonBuilder.endObject();
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(jsonBuilder.bytes());
|
||||
parser.nextToken();
|
||||
factory.parseResult("_id", parser);
|
||||
fail("result parsing should fail if payload is provided but request is missing");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResultParser() throws Exception {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
|
||||
SearchSourceBuilder searchSourceBuilder = searchSource().query(
|
||||
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{ctx.triggered.scheduled_time}}||-30s").to("{{ctx.triggered.triggered_time}}")));
|
||||
SearchRequest request = client()
|
||||
.prepareSearch()
|
||||
.setSearchType(ExecutableSearchInput.DEFAULT_SEARCH_TYPE)
|
||||
.request()
|
||||
.source(searchSourceBuilder);
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(SearchInput.Field.PAYLOAD.getPreferredName(), data);
|
||||
jsonBuilder.field(SearchInput.Field.REQUEST.getPreferredName());
|
||||
WatcherUtils.writeSearchRequest(request, jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
jsonBuilder.endObject();
|
||||
|
||||
SearchInputFactory factory = new SearchInputFactory(settingsBuilder().build(), ClientProxy.of(client()));
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(jsonBuilder.bytes());
|
||||
parser.nextToken();
|
||||
SearchInput.Result result = factory.parseResult("_id", parser);
|
||||
|
||||
assertEquals(SearchInput.TYPE, result.type());
|
||||
assertEquals(result.payload().data().get("foo"), "bar");
|
||||
List baz = (List)result.payload().data().get("baz");
|
||||
assertTrue(baz.isEmpty());
|
||||
assertNotNull(result.executedRequest());
|
||||
}
|
||||
|
||||
private SearchInput.Result executeSearchInput(SearchRequest request) throws IOException {
|
||||
createIndex("test-search-index");
|
||||
ensureGreen("test-search-index");
|
||||
|
|
|
@ -5,17 +5,16 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.input.simple;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.watcher.input.Input;
|
||||
import org.elasticsearch.watcher.input.InputFactory;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
import org.elasticsearch.watcher.input.ExecutableInput;
|
||||
import org.elasticsearch.watcher.input.InputException;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.watcher.input.ExecutableInput;
|
||||
import org.elasticsearch.watcher.input.Input;
|
||||
import org.elasticsearch.watcher.input.InputException;
|
||||
import org.elasticsearch.watcher.input.InputFactory;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -76,35 +75,4 @@ public class SimpleInputTests extends ElasticsearchTestCase {
|
|||
fail("[simple] input parse should fail with an InputException for an empty json object");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResultParser_Valid() throws Exception {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(SimpleInput.Field.PAYLOAD.getPreferredName(), data);
|
||||
jsonBuilder.endObject();
|
||||
|
||||
SimpleInputFactory parser = new SimpleInputFactory(ImmutableSettings.builder().build());
|
||||
SimpleInput.Result staticResult = parser.parseResult("_id", XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
assertEquals(staticResult.type(), SimpleInput.TYPE);
|
||||
|
||||
assertEquals(staticResult.payload().data().get("foo"), "bar");
|
||||
List baz = (List)staticResult.payload().data().get("baz");
|
||||
assertTrue(baz.isEmpty());
|
||||
}
|
||||
|
||||
@Test(expected = InputException.class)
|
||||
public void testResultParser_Invalid() throws Exception {
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.endObject();
|
||||
|
||||
InputFactory parser = new SimpleInputFactory(ImmutableSettings.builder().build());
|
||||
parser.parseResult("_id", XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes()));
|
||||
fail("[simple] input result parse should fail with an InputException for an empty json object");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,8 +43,9 @@ import org.elasticsearch.watcher.actions.email.service.EmailService;
|
|||
import org.elasticsearch.watcher.actions.email.service.Profile;
|
||||
import org.elasticsearch.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.watcher.execution.ExecutionService;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.execution.TriggeredWatchStore;
|
||||
import org.elasticsearch.watcher.license.LicenseService;
|
||||
import org.elasticsearch.watcher.support.clock.ClockMock;
|
||||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
|
@ -53,6 +54,7 @@ import org.elasticsearch.watcher.trigger.ScheduleTriggerEngineMock;
|
|||
import org.elasticsearch.watcher.trigger.TriggerService;
|
||||
import org.elasticsearch.watcher.trigger.schedule.ScheduleModule;
|
||||
import org.elasticsearch.watcher.watch.Watch;
|
||||
import org.elasticsearch.watcher.watch.WatchStore;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
|
@ -279,10 +281,6 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
|
|||
return getInstanceFromMaster(LicenseService.class);
|
||||
}
|
||||
|
||||
protected WatchRecord.Parser watchRecordParser() {
|
||||
return internalTestCluster().getInstance(WatchRecord.Parser.class);
|
||||
}
|
||||
|
||||
protected void assertWatchWithMinimumPerformedActionsCount(final String watchName, final long minimumExpectedWatchActionsWithActionPerformed) throws Exception {
|
||||
assertWatchWithMinimumPerformedActionsCount(watchName, minimumExpectedWatchActionsWithActionPerformed, true);
|
||||
}
|
||||
|
@ -303,7 +301,7 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
|
|||
refresh();
|
||||
SearchResponse searchResponse = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*")
|
||||
.setIndicesOptions(IndicesOptions.lenientExpandOpen())
|
||||
.setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", WatchRecord.State.EXECUTED.id())))
|
||||
.setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", ExecutionState.EXECUTED.id())))
|
||||
.get();
|
||||
assertThat("could not find executed watch record", searchResponse.getHits().getTotalHits(), greaterThanOrEqualTo(minimumExpectedWatchActionsWithActionPerformed));
|
||||
if (assertConditionMet) {
|
||||
|
@ -327,7 +325,7 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
|
|||
refresh();
|
||||
SearchResponse searchResponse = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*")
|
||||
.setIndicesOptions(IndicesOptions.lenientExpandOpen())
|
||||
.setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", WatchRecord.State.EXECUTED.id())))
|
||||
.setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", ExecutionState.EXECUTED.id())))
|
||||
.get();
|
||||
return searchResponse.getHits().getTotalHits();
|
||||
}
|
||||
|
@ -349,14 +347,14 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
|
|||
refresh();
|
||||
SearchResponse searchResponse = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*")
|
||||
.setIndicesOptions(IndicesOptions.lenientExpandOpen())
|
||||
.setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", WatchRecord.State.EXECUTION_NOT_NEEDED.id())))
|
||||
.setQuery(boolQuery().must(matchQuery("watch_id", watchName)).must(matchQuery("state", ExecutionState.EXECUTION_NOT_NEEDED.id())))
|
||||
.get();
|
||||
assertThat(searchResponse.getHits().getTotalHits(), greaterThanOrEqualTo(expectedWatchActionsWithNoActionNeeded));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void assertWatchWithMinimumActionsCount(final String watchName, final WatchRecord.State recordState, final long recordCount) throws Exception {
|
||||
protected void assertWatchWithMinimumActionsCount(final String watchName, final ExecutionState recordState, final long recordCount) throws Exception {
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -494,7 +492,7 @@ public abstract class AbstractWatcherIntegrationTests extends ElasticsearchInteg
|
|||
List<String> templatesToWipe = new ArrayList<>();
|
||||
ClusterState state = client().admin().cluster().prepareState().get().getState();
|
||||
for (ObjectObjectCursor<String, IndexTemplateMetaData> cursor : state.getMetaData().templates()) {
|
||||
if (cursor.key.equals("watches") || cursor.key.equals("watch_history")) {
|
||||
if (cursor.key.equals(WatchStore.INDEX_TEMPLATE) || cursor.key.equals(HistoryStore.INDEX_TEMPLATE_NAME) || cursor.key.equals(TriggeredWatchStore.INDEX_TEMPLATE_NAME)) {
|
||||
continue;
|
||||
}
|
||||
templatesToWipe.add(cursor.key);
|
||||
|
|
|
@ -6,24 +6,21 @@
|
|||
package org.elasticsearch.watcher.test.integration;
|
||||
|
||||
import org.elasticsearch.action.WriteConsistencyLevel;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.elasticsearch.watcher.WatcherState;
|
||||
import org.elasticsearch.watcher.client.WatchSourceBuilder;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.condition.always.AlwaysCondition;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.execution.TriggeredWatch;
|
||||
import org.elasticsearch.watcher.execution.TriggeredWatchStore;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.history.*;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.elasticsearch.watcher.transport.actions.stats.WatcherStatsResponse;
|
||||
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
|
||||
import org.elasticsearch.watcher.watch.Watch;
|
||||
|
@ -31,8 +28,6 @@ import org.elasticsearch.watcher.watch.WatchStore;
|
|||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
|
@ -44,10 +39,8 @@ import static org.elasticsearch.watcher.condition.ConditionBuilders.alwaysCondit
|
|||
import static org.elasticsearch.watcher.condition.ConditionBuilders.scriptCondition;
|
||||
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
|
||||
import static org.elasticsearch.watcher.test.WatcherTestUtils.newInputSearchRequest;
|
||||
import static org.elasticsearch.watcher.transform.TransformBuilders.searchTransform;
|
||||
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.cron;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
|
||||
/**
|
||||
|
@ -133,7 +126,6 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
String index = HistoryStore.getHistoryIndexNameForTime(now);
|
||||
client().prepareIndex(index, HistoryStore.DOC_TYPE, wid.value())
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.field(WatchRecord.Field.WATCH_ID.getPreferredName(), wid.watchId())
|
||||
.startObject(WatchRecord.Field.TRIGGER_EVENT.getPreferredName())
|
||||
.field(event.type(), event)
|
||||
.endObject()
|
||||
|
@ -143,7 +135,6 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
.startObject(Watch.Field.INPUT.getPreferredName())
|
||||
.startObject("none").endObject()
|
||||
.endObject()
|
||||
.field(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.AWAITS_EXECUTION)
|
||||
.endObject())
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.setRefresh(true)
|
||||
|
@ -153,7 +144,6 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
wid = new Wid("_id", 2, now);
|
||||
client().prepareIndex(index, HistoryStore.DOC_TYPE, wid.value())
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.field(WatchRecord.Field.WATCH_ID.getPreferredName(), wid.watchId())
|
||||
.startObject(WatchRecord.Field.TRIGGER_EVENT.getPreferredName())
|
||||
.field(event.type(), event)
|
||||
.endObject()
|
||||
|
@ -163,7 +153,6 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
.startObject(Watch.Field.INPUT.getPreferredName())
|
||||
.startObject("none").endObject()
|
||||
.endObject()
|
||||
.field(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.AWAITS_EXECUTION)
|
||||
.endObject())
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.setRefresh(true)
|
||||
|
@ -173,7 +162,6 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
wid = new Wid("_id", 2, now);
|
||||
client().prepareIndex(index, HistoryStore.DOC_TYPE, wid.value())
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.field(WatchRecord.Field.WATCH_ID.getPreferredName(), wid.watchId())
|
||||
.startObject(WatchRecord.Field.TRIGGER_EVENT.getPreferredName())
|
||||
.startObject("unknown").endObject()
|
||||
.endObject()
|
||||
|
@ -183,7 +171,6 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
.startObject(Watch.Field.INPUT.getPreferredName())
|
||||
.startObject("none").endObject()
|
||||
.endObject()
|
||||
.field(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.AWAITS_EXECUTION)
|
||||
.endObject())
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.setRefresh(true)
|
||||
|
@ -204,20 +191,11 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", now, now);
|
||||
Condition condition = new AlwaysCondition();
|
||||
|
||||
String index = HistoryStore.getHistoryIndexNameForTime(now);
|
||||
client().prepareIndex(index, HistoryStore.DOC_TYPE, wid.value())
|
||||
client().prepareIndex(TriggeredWatchStore.INDEX_NAME, TriggeredWatchStore.DOC_TYPE, wid.value())
|
||||
.setSource(jsonBuilder().startObject()
|
||||
.field(WatchRecord.Field.WATCH_ID.getPreferredName(), wid.value())
|
||||
.startObject(WatchRecord.Field.TRIGGER_EVENT.getPreferredName())
|
||||
.field(event.type(), event)
|
||||
.endObject()
|
||||
.startObject(Watch.Field.CONDITION.getPreferredName())
|
||||
.field(condition.type(), condition)
|
||||
.endObject()
|
||||
.startObject(Watch.Field.INPUT.getPreferredName())
|
||||
.startObject("none").endObject()
|
||||
.endObject()
|
||||
.field(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.AWAITS_EXECUTION)
|
||||
.endObject())
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.setRefresh(true)
|
||||
|
@ -227,10 +205,10 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
startWatcher();
|
||||
|
||||
refresh();
|
||||
SearchResponse searchResponse = client().prepareSearch(index).get();
|
||||
SearchResponse searchResponse = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*").get();
|
||||
assertHitCount(searchResponse, 1);
|
||||
assertThat(searchResponse.getHits().getAt(0).sourceAsMap().get(WatchRecord.Field.WATCH_ID.getPreferredName()).toString(), Matchers.equalTo(wid.value()));
|
||||
assertThat(searchResponse.getHits().getAt(0).sourceAsMap().get(WatchRecord.Field.STATE.getPreferredName()).toString(), Matchers.equalTo(WatchRecord.State.DELETED_WHILE_QUEUED.toString()));
|
||||
assertThat(searchResponse.getHits().getAt(0).id(), Matchers.equalTo(wid.value()));
|
||||
assertThat(searchResponse.getHits().getAt(0).sourceAsMap().get(WatchRecord.Field.STATE.getPreferredName()).toString(), Matchers.equalTo(ExecutionState.DELETED_WHILE_QUEUED.toString()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -261,7 +239,7 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
|
||||
@Test
|
||||
@TestLogging("watcher.actions:DEBUG")
|
||||
public void testWatchRecordLoading() throws Exception {
|
||||
public void testTriggeredWatchLoading() throws Exception {
|
||||
createIndex("output");
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
|
@ -283,10 +261,9 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
now = now.plusMinutes(1);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watchId, now, now);
|
||||
Wid wid = new Wid(watchId, randomLong(), now);
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watchService().getWatch(watchId), event);
|
||||
String index = HistoryStore.getHistoryIndexNameForTime(now);
|
||||
client().prepareIndex(index, HistoryStore.DOC_TYPE, watchRecord.id().value())
|
||||
.setSource(jsonBuilder().value(watchRecord))
|
||||
TriggeredWatch triggeredWatch = new TriggeredWatch(wid, event);
|
||||
client().prepareIndex(TriggeredWatchStore.INDEX_NAME, TriggeredWatchStore.DOC_TYPE, triggeredWatch.id().value())
|
||||
.setSource(jsonBuilder().value(triggeredWatch))
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.get();
|
||||
}
|
||||
|
@ -314,7 +291,7 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMixedWatchRecordLoading() throws Exception {
|
||||
public void testMixedTriggeredWatchLoading() throws Exception {
|
||||
createIndex("output");
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
|
@ -331,40 +308,21 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
).get();
|
||||
|
||||
DateTime now = DateTime.now(UTC);
|
||||
int numRecords = scaledRandomIntBetween(2, 128);
|
||||
int awaitsExecution = 0;
|
||||
final int numRecords = scaledRandomIntBetween(2, 128);
|
||||
for (int i = 0; i < numRecords; i++) {
|
||||
now = now.plusMinutes(1);
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watchId, now, now);
|
||||
Wid wid = new Wid(watchId, randomLong(), now);
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watchService().getWatch(watchId), event);
|
||||
String index = HistoryStore.getHistoryIndexNameForTime(now);
|
||||
client().prepareIndex(index, HistoryStore.DOC_TYPE, watchRecord.id().value())
|
||||
.setSource(jsonBuilder().value(watchRecord))
|
||||
TriggeredWatch triggeredWatch = new TriggeredWatch(wid, event);
|
||||
client().prepareIndex(TriggeredWatchStore.INDEX_NAME, TriggeredWatchStore.DOC_TYPE, triggeredWatch.id().value())
|
||||
.setSource(jsonBuilder().value(triggeredWatch))
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.get();
|
||||
|
||||
final WatchRecord.State state;
|
||||
if (i == 0) {
|
||||
// at least have one record that we need to execute (otherwise the output index doesn't exist)
|
||||
awaitsExecution++;
|
||||
continue;
|
||||
} else {
|
||||
// update to a random state:
|
||||
state = randomFrom(WatchRecord.State.AWAITS_EXECUTION, WatchRecord.State.CHECKING, WatchRecord.State.EXECUTION_NOT_NEEDED, WatchRecord.State.EXECUTED);
|
||||
}
|
||||
client().prepareUpdate(index, HistoryStore.DOC_TYPE, watchRecord.id().value())
|
||||
.setDoc(WatchRecord.Field.STATE.getPreferredName(), state.id())
|
||||
.get();
|
||||
if (state == WatchRecord.State.AWAITS_EXECUTION) {
|
||||
awaitsExecution++;
|
||||
}
|
||||
}
|
||||
|
||||
stopWatcher();
|
||||
startWatcher();
|
||||
|
||||
final int finalAwaitsExecution = awaitsExecution;
|
||||
assertBusy(new Runnable() {
|
||||
|
||||
@Override
|
||||
|
@ -379,70 +337,10 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
|
|||
// the actual documents are in the output index
|
||||
refresh();
|
||||
SearchResponse searchResponse = client().prepareSearch("output").get();
|
||||
assertHitCount(searchResponse, finalAwaitsExecution);
|
||||
assertHitCount(searchResponse, numRecords);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestLogging("watcher.actions:DEBUG")
|
||||
public void testBootStrapManyHistoryIndices() throws Exception {
|
||||
DateTime now = new DateTime(UTC);
|
||||
long numberOfWatchHistoryIndices = randomIntBetween(2, 8);
|
||||
long numberOfWatchRecordsPerIndex = randomIntBetween(5, 10);
|
||||
SearchRequest searchRequest = newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
|
||||
|
||||
for (int i = 0; i < numberOfWatchHistoryIndices; i++) {
|
||||
DateTime historyIndexDate = now.minus((new TimeValue(i, TimeUnit.DAYS)).getMillis());
|
||||
String actionHistoryIndex = HistoryStore.getHistoryIndexNameForTime(historyIndexDate);
|
||||
createIndex(actionHistoryIndex);
|
||||
ensureGreen(actionHistoryIndex);
|
||||
logger.info("Created index {}", actionHistoryIndex);
|
||||
|
||||
for (int j = 0; j < numberOfWatchRecordsPerIndex; j++) {
|
||||
String watchId = "_id" + i + "-" + j;
|
||||
WatchSourceBuilder watchSource = watchBuilder()
|
||||
.trigger(schedule(cron("0/5 * * * * ? 2050")))
|
||||
.input(searchInput(searchRequest))
|
||||
.condition(alwaysCondition())
|
||||
.transform(searchTransform(searchRequest));
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch(watchId).setSource(watchSource).get();
|
||||
assertThat(putWatchResponse.isCreated(), is(true));
|
||||
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent(watchId, historyIndexDate, historyIndexDate);
|
||||
Wid wid = new Wid(watchId, randomLong(), DateTime.now(UTC));
|
||||
WatchRecord watchRecord = new WatchRecord(wid, watchService().getWatch(watchId), event);
|
||||
|
||||
XContentBuilder jsonBuilder2 = jsonBuilder();
|
||||
watchRecord.toXContent(jsonBuilder2, ToXContent.EMPTY_PARAMS);
|
||||
|
||||
IndexResponse indexResponse = client().prepareIndex(actionHistoryIndex, HistoryStore.DOC_TYPE, watchRecord.id().value())
|
||||
.setConsistencyLevel(WriteConsistencyLevel.ALL)
|
||||
.setSource(jsonBuilder2.bytes())
|
||||
.get();
|
||||
assertThat(indexResponse.isCreated(), is(true));
|
||||
}
|
||||
client().admin().indices().prepareRefresh(actionHistoryIndex).get();
|
||||
}
|
||||
|
||||
stopWatcher();
|
||||
startWatcher();
|
||||
WatcherStatsResponse response = watcherClient().prepareWatcherStats().get();
|
||||
|
||||
assertThat(response.getWatcherState(), equalTo(WatcherState.STARTED));
|
||||
final long totalHistoryEntries = numberOfWatchRecordsPerIndex * numberOfWatchHistoryIndices;
|
||||
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
long count = docCount(HistoryStore.INDEX_PREFIX + "*", HistoryStore.DOC_TYPE,
|
||||
termQuery(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.EXECUTED.id()));
|
||||
assertThat(count, is(totalHistoryEntries));
|
||||
}
|
||||
}, 30, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import org.elasticsearch.search.aggregations.Aggregations;
|
|||
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
||||
import org.elasticsearch.watcher.actions.email.service.support.EmailServer;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.junit.After;
|
||||
|
@ -98,7 +98,7 @@ public class HistoryTemplateEmailMappingsTests extends AbstractWatcherIntegratio
|
|||
refresh();
|
||||
|
||||
// the action should fail as no email server is available
|
||||
assertWatchWithMinimumActionsCount("_id", WatchRecord.State.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1);
|
||||
|
||||
SearchResponse response = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*").setSource(searchSource()
|
||||
.aggregation(terms("from").field("execution_result.actions.email.email.from"))
|
||||
|
|
|
@ -12,8 +12,8 @@ import org.elasticsearch.ElasticsearchException;
|
|||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
|
@ -95,7 +95,7 @@ public class HistoryTemplateHttpMappingsTests extends AbstractWatcherIntegration
|
|||
refresh();
|
||||
|
||||
// the action should fail as no email server is available
|
||||
assertWatchWithMinimumActionsCount("_id", WatchRecord.State.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1);
|
||||
|
||||
SearchResponse response = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*").setSource(searchSource()
|
||||
.aggregation(terms("input_result_path").field("execution_result.input.http.request.path"))
|
||||
|
|
|
@ -8,8 +8,8 @@ package org.elasticsearch.watcher.test.integration;
|
|||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.junit.Test;
|
||||
|
@ -55,7 +55,7 @@ public class HistoryTemplateIndexActionMappingsTests extends AbstractWatcherInte
|
|||
refresh();
|
||||
|
||||
// the action should fail as no email server is available
|
||||
assertWatchWithMinimumActionsCount("_id", WatchRecord.State.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1);
|
||||
flush();
|
||||
refresh();
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ import org.elasticsearch.action.search.SearchResponse;
|
|||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.junit.Test;
|
||||
|
@ -67,7 +67,7 @@ public class HistoryTemplateSearchInputMappingsTests extends AbstractWatcherInte
|
|||
refresh();
|
||||
|
||||
// the action should fail as no email server is available
|
||||
assertWatchWithMinimumActionsCount("_id", WatchRecord.State.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1);
|
||||
|
||||
SearchResponse response = client().prepareSearch(HistoryStore.INDEX_PREFIX + "*").setSource(searchSource()
|
||||
.aggregation(terms("input_search_type").field("execution_result.input.search.request.search_type"))
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
|
|||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.hppc.cursors.ObjectObjectCursor;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.junit.Test;
|
||||
|
@ -57,7 +57,7 @@ public class HistoryTemplateTimeMappingsTests extends AbstractWatcherIntegration
|
|||
refresh();
|
||||
|
||||
// the action should fail as no email server is available
|
||||
assertWatchWithMinimumActionsCount("_id", WatchRecord.State.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id", ExecutionState.EXECUTED, 1);
|
||||
refresh();
|
||||
assertBusy(new Runnable() {
|
||||
@Override
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
|
|||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.hppc.cursors.ObjectObjectCursor;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.junit.Test;
|
||||
|
@ -78,8 +78,8 @@ public class HistoryTemplateTransformMappingsTests extends AbstractWatcherIntegr
|
|||
flush();
|
||||
refresh();
|
||||
|
||||
assertWatchWithMinimumActionsCount("_id1", WatchRecord.State.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id2", WatchRecord.State.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id1", ExecutionState.EXECUTED, 1);
|
||||
assertWatchWithMinimumActionsCount("_id2", ExecutionState.EXECUTED, 1);
|
||||
|
||||
refresh();
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.elasticsearch.action.index.IndexResponse;
|
|||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.watcher.actions.ActionStatus;
|
||||
import org.elasticsearch.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
|
@ -127,7 +128,7 @@ public class WatchAckTests extends AbstractWatcherIntegrationTests {
|
|||
assertThat(parsedWatch.status().actionStatus("_a2").ackStatus().state(), is(ActionStatus.AckStatus.State.AWAITS_SUCCESSFUL_EXECUTION));
|
||||
|
||||
long throttledCount = docCount(HistoryStore.INDEX_PREFIX + "*", null,
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.THROTTLED.id()));
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), ExecutionState.THROTTLED.id()));
|
||||
assertThat(throttledCount, greaterThan(0L));
|
||||
}
|
||||
|
||||
|
@ -208,7 +209,7 @@ public class WatchAckTests extends AbstractWatcherIntegrationTests {
|
|||
assertThat(parsedWatch.status().actionStatus("_a2").ackStatus().state(), is(ActionStatus.AckStatus.State.AWAITS_SUCCESSFUL_EXECUTION));
|
||||
|
||||
long throttledCount = docCount(HistoryStore.INDEX_PREFIX + "*", null,
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.THROTTLED.id()));
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), ExecutionState.THROTTLED.id()));
|
||||
assertThat(throttledCount, greaterThan(0L));
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.junit.Test;
|
|||
import static org.elasticsearch.watcher.actions.ActionBuilders.loggingAction;
|
||||
import static org.elasticsearch.watcher.client.WatchSourceBuilders.watchBuilder;
|
||||
import static org.elasticsearch.watcher.condition.ConditionBuilders.scriptCondition;
|
||||
import static org.elasticsearch.watcher.input.InputBuilders.simpleInput;
|
||||
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
@ -30,20 +29,21 @@ public class WatchForceDeleteTests extends AbstractWatcherIntegrationTests {
|
|||
return false; //Disable time warping for the force delete long running watch test
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enableShield() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Slow
|
||||
public void testForceDelete_LongRunningWatch() throws Exception {
|
||||
PutWatchResponse putResponse = watcherClient().preparePutWatch("_name").setSource(watchBuilder()
|
||||
.trigger(schedule(interval("1s")))
|
||||
.input(simpleInput())
|
||||
.condition(scriptCondition(Script.inline("sleep 5000; return true")))
|
||||
.addAction("_action1", loggingAction("{{ctx.watch_id}}")))
|
||||
.get();
|
||||
assertThat(putResponse.getId(), equalTo("_name"));
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
DeleteWatchResponse deleteWatchResponse = watcherClient().prepareDeleteWatch("_name").setForce(true).get();
|
||||
assertThat(deleteWatchResponse.isFound(), is(true));
|
||||
deleteWatchResponse = watcherClient().prepareDeleteWatch("_name").get();
|
||||
|
|
|
@ -12,8 +12,8 @@ import org.elasticsearch.watcher.actions.logging.LoggingLevel;
|
|||
import org.elasticsearch.watcher.condition.always.AlwaysCondition;
|
||||
import org.elasticsearch.watcher.execution.ActionExecutionMode;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
import org.elasticsearch.watcher.support.xcontent.MapPath;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.test.WatcherTestUtils;
|
||||
import org.elasticsearch.watcher.transport.actions.execute.ExecuteWatchResponse;
|
||||
|
@ -36,7 +36,8 @@ import static org.elasticsearch.watcher.condition.ConditionBuilders.scriptCondit
|
|||
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
|
||||
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.cron;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -95,14 +96,12 @@ public class WatchMetadataTests extends AbstractWatcherIntegrationTests {
|
|||
.metadata(metadata))
|
||||
.get();
|
||||
|
||||
WatchRecord.Parser parser = getInstanceFromMaster(WatchRecord.Parser.class);
|
||||
TriggerEvent triggerEvent = new ScheduleTriggerEvent(new DateTime(UTC), new DateTime(UTC));
|
||||
ExecuteWatchResponse executeWatchResponse = watcherClient().prepareExecuteWatch("_name").setTriggerEvent(triggerEvent).setActionMode("_all", ActionExecutionMode.SIMULATE).get();
|
||||
Map<String, Object> result = executeWatchResponse.getRecordSource().getAsMap();;
|
||||
|
||||
WatchRecord record = parser.parse("test_run", 1, executeWatchResponse.getRecordSource().getBytes());
|
||||
assertThat(record.metadata().get("foo").toString(), equalTo("bar"));
|
||||
assertThat(record.execution().actionsResults().get("testLogger").action(), instanceOf(LoggingAction.Result.Simulated.class));
|
||||
LoggingAction.Result.Simulated simulatedResult = (LoggingAction.Result.Simulated) (record.execution().actionsResults().get("testLogger").action());
|
||||
assertThat(simulatedResult.loggedText(), equalTo("This is a test"));
|
||||
assertThat(MapPath.<String>eval("metadata.foo", result), equalTo("bar"));
|
||||
assertThat(MapPath.<String>eval("execution_result.actions.0.id", result), equalTo("testLogger"));
|
||||
assertThat(MapPath.<String>eval("execution_result.actions.0.logging.logged_text", result), equalTo("This is a test"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.action.index.IndexResponse;
|
|||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.watcher.execution.ExecutionState;
|
||||
import org.elasticsearch.watcher.history.HistoryStore;
|
||||
import org.elasticsearch.watcher.history.WatchRecord;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
|
@ -94,7 +95,7 @@ public class WatchTimeThrottleTests extends AbstractWatcherIntegrationTests {
|
|||
assertThat(actionsCount, is(2L));
|
||||
|
||||
long throttledCount = docCount(HistoryStore.INDEX_PREFIX + "*", null,
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.THROTTLED.id()));
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), ExecutionState.THROTTLED.id()));
|
||||
assertThat(throttledCount, is(1L));
|
||||
|
||||
} else {
|
||||
|
@ -118,7 +119,7 @@ public class WatchTimeThrottleTests extends AbstractWatcherIntegrationTests {
|
|||
assertThat(actionsCount, is(1L));
|
||||
|
||||
long throttledCount = docCount(HistoryStore.INDEX_PREFIX + "*", null,
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.THROTTLED.id()));
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), ExecutionState.THROTTLED.id()));
|
||||
assertThat(throttledCount, greaterThanOrEqualTo(1L));
|
||||
}
|
||||
}, 5, TimeUnit.SECONDS);
|
||||
|
@ -168,7 +169,7 @@ public class WatchTimeThrottleTests extends AbstractWatcherIntegrationTests {
|
|||
assertThat(actionsCount, is(2L));
|
||||
|
||||
long throttledCount = docCount(HistoryStore.INDEX_PREFIX + "*", null,
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), WatchRecord.State.THROTTLED.id()));
|
||||
matchQuery(WatchRecord.Field.STATE.getPreferredName(), ExecutionState.THROTTLED.id()));
|
||||
assertThat(throttledCount, is(1L));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,19 +172,6 @@ public class ChainTransformTests extends ElasticsearchTestCase {
|
|||
return new Transform(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result parseResult(String watchId, XContentParser parser) throws IOException {
|
||||
assert parser.currentToken() == XContentParser.Token.START_OBJECT;
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.FIELD_NAME; // the "payload" field
|
||||
token = parser.nextToken();
|
||||
assert token == XContentParser.Token.START_OBJECT;
|
||||
Payload payload = new Payload.XContent(parser);
|
||||
token = parser.nextToken();
|
||||
assert token == XContentParser.Token.END_OBJECT;
|
||||
return new Result("named", payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedExecutableTransform createExecutable(Transform transform) {
|
||||
return new NamedExecutableTransform(transform);
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.elasticsearch.common.collect.ImmutableMap;
|
|||
import org.elasticsearch.common.joda.time.DateTime;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
|
@ -30,7 +29,6 @@ import org.elasticsearch.watcher.execution.TriggeredExecutionContext;
|
|||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.input.simple.ExecutableSimpleInput;
|
||||
import org.elasticsearch.watcher.input.simple.SimpleInput;
|
||||
import org.elasticsearch.watcher.support.WatcherUtils;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
import org.elasticsearch.watcher.transform.Transform;
|
||||
|
@ -46,7 +44,6 @@ import org.junit.Test;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
|
||||
|
@ -357,41 +354,6 @@ public class SearchTransformTests extends ElasticsearchIntegrationTest {
|
|||
assertThat(result.executedRequest().indicesOptions(), equalTo(request.indicesOptions()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResultParser() throws Exception {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("foo", "bar");
|
||||
data.put("baz", new ArrayList<String>() );
|
||||
|
||||
SearchSourceBuilder searchSourceBuilder = searchSource().query(
|
||||
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{ctx.triggered.scheduled_time}}||-30s").to("{{ctx.triggered.triggered_time}}")));
|
||||
SearchRequest request = client()
|
||||
.prepareSearch()
|
||||
.setSearchType(ExecutableSearchTransform.DEFAULT_SEARCH_TYPE)
|
||||
.request()
|
||||
.source(searchSourceBuilder);
|
||||
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
jsonBuilder.startObject();
|
||||
jsonBuilder.field(SearchTransform.Field.PAYLOAD.getPreferredName(), data);
|
||||
jsonBuilder.field(SearchTransform.Field.REQUEST.getPreferredName());
|
||||
WatcherUtils.writeSearchRequest(request, jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
jsonBuilder.endObject();
|
||||
|
||||
SearchTransformFactory factory = new SearchTransformFactory(settingsBuilder().build(), ClientProxy.of(client()));
|
||||
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(jsonBuilder.bytes());
|
||||
parser.nextToken();
|
||||
SearchTransform.Result result = factory.parseResult("_id", parser);
|
||||
|
||||
assertEquals(SearchTransform.TYPE, result.type());
|
||||
assertEquals(result.payload().data().get("foo"), "bar");
|
||||
List baz = (List)result.payload().data().get("baz");
|
||||
assertTrue(baz.isEmpty());
|
||||
assertNotNull(result.executedRequest());
|
||||
}
|
||||
|
||||
|
||||
private SearchTransform.Result executeSearchTransform(SearchRequest request) throws IOException {
|
||||
createIndex("test-search-index");
|
||||
ensureGreen("test-search-index");
|
||||
|
|
Loading…
Reference in New Issue