[client] reorganized tests and added alert source builder

The `AlertSourceBuilder` along with a set of source builder for all the different constructs that make an alert (condition, input, transform and action), provides a structured approach for building an alert from the client side (instead of forcing the clients to use xcontent directory)

- fixed some of the tests to already use these builders (I reckon there are still quite a few that need to be converted.. but we'll do that over time).
- moved all integration tests under `test/integration` package.
- changed the `AlertsTests` to **not** be an integration test... it randomizes the alert structure and makes sure that it can serialize & deserialize itself to/from xcontent.
- fixed small bugs found by the tests

Original commit: elastic/x-pack-elasticsearch@94b76b6fc7
This commit is contained in:
uboness 2015-02-25 22:05:11 +02:00
parent b76b0e7129
commit aae6ff834f
55 changed files with 1662 additions and 531 deletions

View File

@ -7,11 +7,13 @@ package org.elasticsearch.alerts;
import org.elasticsearch.alerts.actions.ActionRegistry; import org.elasticsearch.alerts.actions.ActionRegistry;
import org.elasticsearch.alerts.actions.Actions; import org.elasticsearch.alerts.actions.Actions;
import org.elasticsearch.alerts.scheduler.Scheduler;
import org.elasticsearch.alerts.condition.Condition; import org.elasticsearch.alerts.condition.Condition;
import org.elasticsearch.alerts.condition.ConditionRegistry; import org.elasticsearch.alerts.condition.ConditionRegistry;
import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.alerts.input.Input; import org.elasticsearch.alerts.input.Input;
import org.elasticsearch.alerts.input.InputRegistry; import org.elasticsearch.alerts.input.InputRegistry;
import org.elasticsearch.alerts.input.NoneInput;
import org.elasticsearch.alerts.scheduler.Scheduler;
import org.elasticsearch.alerts.scheduler.schedule.Schedule; import org.elasticsearch.alerts.scheduler.schedule.Schedule;
import org.elasticsearch.alerts.scheduler.schedule.ScheduleRegistry; import org.elasticsearch.alerts.scheduler.schedule.ScheduleRegistry;
import org.elasticsearch.alerts.throttle.AlertThrottler; import org.elasticsearch.alerts.throttle.AlertThrottler;
@ -27,6 +29,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.joda.time.DateTimeZone;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
@ -57,7 +60,7 @@ public class Alert implements Scheduler.Job, ToXContent {
@Nullable @Nullable
private final Transform transform; private final Transform transform;
public Alert(String name, Schedule schedule, Input input, Condition condition, Transform transform, Actions actions, Map<String, Object> metadata, Status status, TimeValue throttlePeriod) { public Alert(String name, Schedule schedule, Input input, Condition condition, @Nullable Transform transform, Actions actions, @Nullable Map<String, Object> metadata, @Nullable TimeValue throttlePeriod, @Nullable Status status) {
this.name = name; this.name = name;
this.schedule = schedule; this.schedule = schedule;
this.input = input; this.input = input;
@ -67,7 +70,6 @@ public class Alert implements Scheduler.Job, ToXContent {
this.throttlePeriod = throttlePeriod; this.throttlePeriod = throttlePeriod;
this.metadata = metadata; this.metadata = metadata;
this.transform = transform != null ? transform : Transform.NOOP; this.transform = transform != null ? transform : Transform.NOOP;
throttler = new AlertThrottler(throttlePeriod); throttler = new AlertThrottler(throttlePeriod);
} }
@ -174,6 +176,9 @@ public class Alert implements Scheduler.Job, ToXContent {
private final ActionRegistry actionRegistry; private final ActionRegistry actionRegistry;
private final InputRegistry inputRegistry; private final InputRegistry inputRegistry;
private final Input defaultInput;
private final Condition defaultCondition;
@Inject @Inject
public Parser(Settings settings, ConditionRegistry conditionRegistry, ScheduleRegistry scheduleRegistry, public Parser(Settings settings, ConditionRegistry conditionRegistry, ScheduleRegistry scheduleRegistry,
TransformRegistry transformRegistry, ActionRegistry actionRegistry, TransformRegistry transformRegistry, ActionRegistry actionRegistry,
@ -185,6 +190,9 @@ public class Alert implements Scheduler.Job, ToXContent {
this.transformRegistry = transformRegistry; this.transformRegistry = transformRegistry;
this.actionRegistry = actionRegistry; this.actionRegistry = actionRegistry;
this.inputRegistry = inputRegistry; this.inputRegistry = inputRegistry;
this.defaultInput = new NoneInput(logger);
this.defaultCondition = new AlwaysTrueCondition(logger);
} }
public Alert parse(String name, boolean includeStatus, BytesReference source) { public Alert parse(String name, boolean includeStatus, BytesReference source) {
@ -200,8 +208,8 @@ public class Alert implements Scheduler.Job, ToXContent {
public Alert parse(String name, boolean includeStatus, XContentParser parser) throws IOException { public Alert parse(String name, boolean includeStatus, XContentParser parser) throws IOException {
Schedule schedule = null; Schedule schedule = null;
Input input = null; Input input = defaultInput;
Condition condition = null; Condition condition = defaultCondition;
Actions actions = null; Actions actions = null;
Transform transform = null; Transform transform = null;
Map<String, Object> metatdata = null; Map<String, Object> metatdata = null;
@ -244,30 +252,24 @@ public class Alert implements Scheduler.Job, ToXContent {
if (schedule == null) { if (schedule == null) {
throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert schedule"); throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert schedule");
} }
if (input == null) {
throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert input");
}
if (condition == null) {
throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert condition");
}
if (actions == null) { if (actions == null) {
throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert actions"); throw new AlertsSettingsException("could not parse alert [" + name + "]. missing alert actions");
} }
return new Alert(name, schedule, input, condition, transform, actions, metatdata, status, throttlePeriod); return new Alert(name, schedule, input, condition, transform, actions, metatdata, throttlePeriod, status);
} }
} }
public static class Status implements ToXContent, Streamable { public static class Status implements ToXContent, Streamable {
public static final ParseField TIMESTAMP_FIELD = new ParseField("last_throttled");
public static final ParseField LAST_CHECKED_FIELD = new ParseField("last_checked"); public static final ParseField LAST_CHECKED_FIELD = new ParseField("last_checked");
public static final ParseField LAST_MET_CONDITION_FIELD = new ParseField("last_met_condition"); public static final ParseField LAST_MET_CONDITION_FIELD = new ParseField("last_met_condition");
public static final ParseField LAST_THROTTLED_FIELD = new ParseField("last_throttled"); public static final ParseField LAST_THROTTLED_FIELD = new ParseField("last_throttled");
public static final ParseField LAST_EXECUTED_FIELD = new ParseField("last_executed"); public static final ParseField LAST_EXECUTED_FIELD = new ParseField("last_executed");
public static final ParseField ACK_FIELD = new ParseField("ack"); public static final ParseField ACK_FIELD = new ParseField("ack");
public static final ParseField STATE_FIELD = new ParseField("state"); public static final ParseField STATE_FIELD = new ParseField("state");
public static final ParseField TIMESTAMP_FIELD = new ParseField("timestamp");
public static final ParseField REASON_FIELD = new ParseField("reason"); public static final ParseField REASON_FIELD = new ParseField("reason");
private transient long version; private transient long version;
@ -335,6 +337,38 @@ public class Alert implements Scheduler.Job, ToXContent {
return ackStatus; return ackStatus;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Status status = (Status) o;
if (version != status.version) return false;
if (!ackStatus.equals(status.ackStatus)) return false;
if (lastChecked != null ? !lastChecked.equals(status.lastChecked) : status.lastChecked != null)
return false;
if (lastExecuted != null ? !lastExecuted.equals(status.lastExecuted) : status.lastExecuted != null)
return false;
if (lastMetCondition != null ? !lastMetCondition.equals(status.lastMetCondition) : status.lastMetCondition != null)
return false;
if (lastThrottle != null ? !lastThrottle.equals(status.lastThrottle) : status.lastThrottle != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = (int) (version ^ (version >>> 32));
result = 31 * result + (lastChecked != null ? lastChecked.hashCode() : 0);
result = 31 * result + (lastMetCondition != null ? lastMetCondition.hashCode() : 0);
result = 31 * result + (lastThrottle != null ? lastThrottle.hashCode() : 0);
result = 31 * result + (lastExecuted != null ? lastExecuted.hashCode() : 0);
result = 31 * result + ackStatus.hashCode();
return result;
}
/** /**
* Called whenever an alert is checked, ie. the condition of the alert is evaluated to see if * Called whenever an alert is checked, ie. the condition of the alert is evaluated to see if
* the alert should be executed. * the alert should be executed.
@ -534,7 +568,7 @@ public class Alert implements Scheduler.Job, ToXContent {
private final DateTime timestamp; private final DateTime timestamp;
public AckStatus() { public AckStatus() {
this(State.AWAITS_EXECUTION, new DateTime()); this(State.AWAITS_EXECUTION, new DateTime(DateTimeZone.UTC));
} }
public AckStatus(State state, DateTime timestamp) { public AckStatus(State state, DateTime timestamp) {
@ -550,6 +584,25 @@ public class Alert implements Scheduler.Job, ToXContent {
return timestamp; return timestamp;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AckStatus ackStatus = (AckStatus) o;
if (state != ackStatus.state) return false;
if (!timestamp.equals(ackStatus.timestamp)) return false;
return true;
}
@Override
public int hashCode() {
int result = state.hashCode();
result = 31 * result + timestamp.hashCode();
return result;
}
} }
public static class Throttle { public static class Throttle {

View File

@ -43,6 +43,23 @@ public interface Payload extends ToXContent {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.value(data); return builder.value(data);
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Simple simple = (Simple) o;
if (!data.equals(simple.data)) return false;
return true;
}
@Override
public int hashCode() {
return data.hashCode();
}
} }
static class ActionResponse extends Simple { static class ActionResponse extends Simple {
@ -50,7 +67,6 @@ public interface Payload extends ToXContent {
public ActionResponse(org.elasticsearch.action.ActionResponse response) { public ActionResponse(org.elasticsearch.action.ActionResponse response) {
super(responseToData(response)); super(responseToData(response));
} }
} }
static class XContent extends Simple { static class XContent extends Simple {

View File

@ -48,7 +48,7 @@ public abstract class Action<R extends Action.Result> implements ToXContent {
/** /**
* Parses xcontent to a concrete action of the same type. * Parses xcontent to a concrete action of the same type.
*/ */
protected static interface Parser<R extends Result, T extends Action<R>> { public static interface Parser<R extends Result, T extends Action<R>> {
/** /**
* @return The type of the action * @return The type of the action
@ -92,5 +92,12 @@ public abstract class Action<R extends Action.Result> implements ToXContent {
} }
protected abstract XContentBuilder xContentBody(XContentBuilder builder, Params params) throws IOException; protected abstract XContentBuilder xContentBody(XContentBuilder builder, Params params) throws IOException;
}
public static interface SourceBuilder extends ToXContent {
String type();
} }
} }

View File

@ -0,0 +1,36 @@
/*
* 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.alerts.actions;
import org.elasticsearch.alerts.actions.email.EmailAction;
import org.elasticsearch.alerts.actions.index.IndexAction;
import org.elasticsearch.alerts.actions.webhook.WebhookAction;
import org.elasticsearch.alerts.support.Script;
/**
*
*/
public final class ActionBuilders {
private ActionBuilders() {
}
public static EmailAction.SourceBuilder emailAction() {
return new EmailAction.SourceBuilder();
}
public static IndexAction.SourceBuilder indexAction(String index, String type) {
return new IndexAction.SourceBuilder(index, type);
}
public static WebhookAction.SourceBuilder webhookAction(String url) {
return new WebhookAction.SourceBuilder(url);
}
public static WebhookAction.SourceBuilder webhookAction(Script url) {
return new WebhookAction.SourceBuilder(url);
}
}

View File

@ -23,6 +23,10 @@ public class Actions implements Iterable<Action>, ToXContent {
this.actions = actions; this.actions = actions;
} }
public int count() {
return actions.size();
}
@Override @Override
public Iterator<Action> iterator() { public Iterator<Action> iterator() {
return actions.iterator(); return actions.iterator();
@ -38,4 +42,20 @@ public class Actions implements Iterable<Action>, ToXContent {
return builder; return builder;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Actions actions1 = (Actions) o;
if (!actions.equals(actions1.actions)) return false;
return true;
}
@Override
public int hashCode() {
return actions.hashCode();
}
} }

View File

@ -358,6 +358,68 @@ public class EmailAction extends Action<EmailAction.Result> {
} }
} }
public static class SourceBuilder implements Action.SourceBuilder {
private Email.Address from;
private Email.AddressList replyTo;
private Email.AddressList to;
private Email.AddressList cc;
private Email.AddressList bcc;
private Authentication auth = null;
private Profile profile = null;
private String account = null;
private Template subject;
private Template textBody;
private Template htmlBody;
private Boolean attachPayload;
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (from != null) {
builder.field(Email.FROM_FIELD.getPreferredName(), from);
}
if (replyTo != null && replyTo.size() != 0) {
builder.field(Email.REPLY_TO_FIELD.getPreferredName(), (ToXContent) replyTo);
}
if (to != null && to.size() != 0) {
builder.field(Email.TO_FIELD.getPreferredName(), (ToXContent) to);
}
if (cc != null && cc.size() != 0) {
builder.field(Email.CC_FIELD.getPreferredName(), (ToXContent) cc);
}
if (bcc != null && bcc.size() != 0) {
builder.field(Email.BCC_FIELD.getPreferredName(), (ToXContent) bcc);
}
if (auth != null) {
builder.field(Parser.USER_FIELD.getPreferredName(), auth.user());
builder.field(Parser.PASSWORD_FIELD.getPreferredName(), auth.password());
}
if (profile != null) {
builder.field(Parser.PROFILE_FIELD.getPreferredName(), profile);
}
if (account != null) {
builder.field(Parser.ACCOUNT_FIELD.getPreferredName(), account);
}
if (subject != null) {
builder.field(Email.SUBJECT_FIELD.getPreferredName(), subject);
}
if (textBody != null) {
builder.field(Email.TEXT_BODY_FIELD.getPreferredName(), textBody);
}
if (htmlBody != null) {
builder.field(Email.HTML_BODY_FIELD.getPreferredName(), htmlBody);
}
if (attachPayload != null) {
builder.field(Parser.ATTACH_PAYLOAD_FIELD.getPreferredName(), attachPayload);
}
return builder.endObject();
}
}
} }

View File

@ -233,7 +233,32 @@ public class IndexAction extends Action<IndexAction.Result> {
} }
return builder; return builder;
} }
}
public static class SourceBuilder implements Action.SourceBuilder {
private final String index;
private final String type;
public SourceBuilder(String index, String type) {
this.index = index;
this.type = type;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject()
.field(Parser.INDEX_FIELD.getPreferredName(), index)
.field(Parser.TYPE_FIELD.getPreferredName(), type)
.endObject();
}
} }
} }

View File

@ -11,6 +11,7 @@ import org.elasticsearch.alerts.Payload;
import org.elasticsearch.alerts.actions.Action; import org.elasticsearch.alerts.actions.Action;
import org.elasticsearch.alerts.actions.ActionException; import org.elasticsearch.alerts.actions.ActionException;
import org.elasticsearch.alerts.actions.ActionSettingsException; import org.elasticsearch.alerts.actions.ActionSettingsException;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.template.Template; import org.elasticsearch.alerts.support.template.Template;
import org.elasticsearch.alerts.support.template.XContentTemplate; import org.elasticsearch.alerts.support.template.XContentTemplate;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
@ -25,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
/** /**
*/ */
@ -75,13 +77,12 @@ public class WebhookAction extends Action<WebhookAction.Result> {
logger.error("failed to connect to [{}] for alert [{}]", ioe, urlText, ctx.alert().name()); logger.error("failed to connect to [{}] for alert [{}]", ioe, urlText, ctx.alert().name());
return new Result.Failure("failed to send http request. " + ioe.getMessage()); return new Result.Failure("failed to send http request. " + ioe.getMessage());
} }
} }
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(); builder.startObject();
builder.field(Parser.METHOD_FIELD.getPreferredName(), method.getName()); builder.field(Parser.METHOD_FIELD.getPreferredName(), method.getName().toLowerCase(Locale.ROOT));
builder.field(Parser.URL_FIELD.getPreferredName(), url); builder.field(Parser.URL_FIELD.getPreferredName(), url);
if (body != null) { if (body != null) {
builder.field(Parser.BODY_FIELD.getPreferredName(), body); builder.field(Parser.BODY_FIELD.getPreferredName(), body);
@ -96,7 +97,7 @@ public class WebhookAction extends Action<WebhookAction.Result> {
WebhookAction that = (WebhookAction) o; WebhookAction that = (WebhookAction) o;
if (!body.equals(that.body)) return false; if (body != null ? !body.equals(that.body) : that.body != null) return false;
if (!method.equals(that.method)) return false; if (!method.equals(that.method)) return false;
if (!url.equals(that.url)) return false; if (!url.equals(that.url)) return false;
@ -107,7 +108,7 @@ public class WebhookAction extends Action<WebhookAction.Result> {
public int hashCode() { public int hashCode() {
int result = method.hashCode(); int result = method.hashCode();
result = 31 * result + url.hashCode(); result = 31 * result + url.hashCode();
result = 31 * result + body.hashCode(); result = 31 * result + (body != null ? body.hashCode() : 0);
return result; return result;
} }
@ -205,7 +206,7 @@ public class WebhookAction extends Action<WebhookAction.Result> {
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
} else if ((token.isValue() || token == XContentParser.Token.START_OBJECT) && currentFieldName != null ) { } else if ((token.isValue() || token == XContentParser.Token.START_OBJECT) && currentFieldName != null ) {
if (METHOD_FIELD.match(currentFieldName)) { if (METHOD_FIELD.match(currentFieldName)) {
method = HttpMethod.valueOf(parser.text()); method = HttpMethod.valueOf(parser.text().toUpperCase(Locale.ROOT));
if (method != HttpMethod.POST && method != HttpMethod.GET && method != HttpMethod.PUT) { if (method != HttpMethod.POST && method != HttpMethod.GET && method != HttpMethod.PUT) {
throw new ActionSettingsException("could not parse webhook action. unsupported http method [" + method.getName() + "]"); throw new ActionSettingsException("could not parse webhook action. unsupported http method [" + method.getName() + "]");
} }
@ -279,4 +280,46 @@ public class WebhookAction extends Action<WebhookAction.Result> {
} }
} }
public static class SourceBuilder implements Action.SourceBuilder {
private final Script url;
private HttpMethod method;
private Script body = null;
public SourceBuilder(String url) {
this(new Script(url));
}
public SourceBuilder(Script url) {
this.url = url;
}
public SourceBuilder method(HttpMethod method) {
this.method = method;
return this;
}
public SourceBuilder body(Script body) {
this.body = body;
return this;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(Parser.URL_FIELD.getPreferredName(), url);
if (method != null) {
builder.field(Parser.METHOD_FIELD.getPreferredName(), method.getName().toLowerCase(Locale.ROOT));
}
if (body != null) {
builder.field(Parser.BODY_FIELD.getPreferredName(), body);
}
return builder.endObject();
}
}
} }

View File

@ -0,0 +1,129 @@
/*
* 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.alerts.client;
import org.elasticsearch.alerts.Alert;
import org.elasticsearch.alerts.actions.Action;
import org.elasticsearch.alerts.condition.Condition;
import org.elasticsearch.alerts.condition.ConditionBuilders;
import org.elasticsearch.alerts.input.Input;
import org.elasticsearch.alerts.input.NoneInput;
import org.elasticsearch.alerts.scheduler.schedule.Schedule;
import org.elasticsearch.alerts.transform.Transform;
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.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.builder.SearchSourceBuilderException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
*
*/
public class AlertSourceBuilder implements ToXContent {
public static AlertSourceBuilder alertSourceBuilder() {
return new AlertSourceBuilder();
}
private Schedule schedule;
private Input.SourceBuilder input = NoneInput.SourceBuilder.INSTANCE;
private Condition.SourceBuilder condition = ConditionBuilders.alwaysTrueCondition();
private Transform.SourceBuilder transform = null;
private Set<Action.SourceBuilder> actions = new HashSet<>();
private TimeValue throttlePeriod = null;
private Map<String, Object> metadata;
public AlertSourceBuilder schedule(Schedule schedule) {
this.schedule = schedule;
return this;
}
public AlertSourceBuilder input(Input.SourceBuilder input) {
this.input = input;
return this;
}
public AlertSourceBuilder condition(Condition.SourceBuilder condition) {
this.condition = condition;
return this;
}
public AlertSourceBuilder transform(Transform.SourceBuilder transform) {
this.transform = transform;
return this;
}
public AlertSourceBuilder throttlePeriod(TimeValue throttlePeriod) {
this.throttlePeriod = throttlePeriod;
return this;
}
public AlertSourceBuilder addAction(Action.SourceBuilder action) {
actions.add(action);
return this;
}
public AlertSourceBuilder metadata(Map<String, Object> metadata) {
this.metadata = metadata;
return this;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.startObject(Alert.Parser.SCHEDULE_FIELD.getPreferredName())
.field(schedule.type(), schedule)
.endObject();
builder.startObject(Alert.Parser.INPUT_FIELD.getPreferredName())
.field(input.type(), input)
.endObject();
builder.startObject(Alert.Parser.CONDITION_FIELD.getPreferredName())
.field(condition.type(), condition)
.endObject();
if (transform != null) {
builder.startObject(Alert.Parser.TRANSFORM_FIELD.getPreferredName())
.field(transform.type(), transform)
.endObject();
}
if (throttlePeriod != null) {
builder.field(Alert.Parser.THROTTLE_PERIOD_FIELD.getPreferredName(), throttlePeriod.getMillis());
}
builder.startArray(Alert.Parser.ACTIONS_FIELD.getPreferredName());
for (Action.SourceBuilder action : actions) {
builder.startObject().field(action.type(), action).endObject();
}
builder.endArray();
if (metadata != null) {
builder.field(Alert.Parser.META_FIELD.getPreferredName(), metadata);
}
return builder.endObject();
}
public BytesReference buildAsBytes(XContentType contentType) throws SearchSourceBuilderException {
try {
XContentBuilder builder = XContentFactory.contentBuilder(contentType);
toXContent(builder, ToXContent.EMPTY_PARAMS);
return builder.bytes();
} catch (Exception e) {
throw new SearchSourceBuilderException("Failed to build search source", e);
}
}
}

View File

@ -75,4 +75,10 @@ public abstract class Condition<R extends Condition.Result> implements ToXConten
public boolean met() { return met; } public boolean met() { return met; }
} }
public static interface SourceBuilder extends ToXContent {
public String type();
}
} }

View File

@ -0,0 +1,31 @@
/*
* 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.alerts.condition;
import org.elasticsearch.alerts.condition.script.ScriptCondition;
import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition;
/**
*
*/
public final class ConditionBuilders {
private ConditionBuilders() {
}
public static AlwaysTrueCondition.SourceBuilder alwaysTrueCondition() {
return AlwaysTrueCondition.SourceBuilder.INSTANCE;
}
public static ScriptCondition.SourceBuilder scriptCondition() {
return new ScriptCondition.SourceBuilder();
}
public static ScriptCondition.SourceBuilder scriptCondition(String script) {
return new ScriptCondition.SourceBuilder().script(script);
}
}

View File

@ -11,6 +11,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* *
@ -24,6 +25,10 @@ public class ConditionRegistry {
this.parsers = ImmutableMap.copyOf(parsers); this.parsers = ImmutableMap.copyOf(parsers);
} }
public Set<String> types() {
return parsers.keySet();
}
/** /**
* Reads the contents of parser to create the correct Condition * Reads the contents of parser to create the correct Condition
* *

View File

@ -18,8 +18,11 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.ScriptService;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.Map;
/** /**
* This class executes a script against the ctx payload and returns a boolean * This class executes a script against the ctx payload and returns a boolean
@ -61,6 +64,23 @@ public class ScriptCondition extends Condition<ScriptCondition.Result> {
return script.toXContent(builder, params); return script.toXContent(builder, params);
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ScriptCondition that = (ScriptCondition) o;
if (!script.equals(that.script)) return false;
return true;
}
@Override
public int hashCode() {
return script.hashCode();
}
public static class Parser extends AbstractComponent implements Condition.Parser<Result, ScriptCondition> { public static class Parser extends AbstractComponent implements Condition.Parser<Result, ScriptCondition> {
private final ScriptServiceProxy scriptService; private final ScriptServiceProxy scriptService;
@ -131,6 +151,43 @@ public class ScriptCondition extends Condition<ScriptCondition.Result> {
.field(MET_FIELD.getPreferredName(), met()) .field(MET_FIELD.getPreferredName(), met())
.endObject(); .endObject();
} }
}
public static class SourceBuilder implements Condition.SourceBuilder {
private String script;
private String lang = ScriptService.DEFAULT_LANG;
private ScriptService.ScriptType type = ScriptService.ScriptType.INLINE;
private Map<String, Object> params = Collections.emptyMap();
public SourceBuilder script(String script) {
this.script = script;
return this;
}
public SourceBuilder lang(String lang) {
this.lang = lang;
return this;
}
public SourceBuilder type(ScriptService.ScriptType type) {
this.type = type;
return this;
}
public SourceBuilder type(Map<String, Object> params) {
this.params = params;
return this;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return new Script(script, type, lang, this.params).toXContent(builder, params);
}
} }
} }

View File

@ -12,6 +12,7 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
@ -22,6 +23,7 @@ import java.io.IOException;
public class AlwaysFalseCondition extends Condition<Condition.Result> { public class AlwaysFalseCondition extends Condition<Condition.Result> {
public static final String TYPE = "always_false"; public static final String TYPE = "always_false";
public static final Result RESULT = new Result(TYPE, false) { public static final Result RESULT = new Result(TYPE, false) {
@Override @Override
@ -50,6 +52,11 @@ public class AlwaysFalseCondition extends Condition<Condition.Result> {
return builder.startObject().endObject(); return builder.startObject().endObject();
} }
@Override
public boolean equals(Object obj) {
return obj instanceof AlwaysFalseCondition;
}
public static class Parser extends AbstractComponent implements Condition.Parser<Result, AlwaysFalseCondition> { public static class Parser extends AbstractComponent implements Condition.Parser<Result, AlwaysFalseCondition> {
@Inject @Inject
@ -80,4 +87,22 @@ public class AlwaysFalseCondition extends Condition<Condition.Result> {
} }
} }
public static class SourceBuilder implements Condition.SourceBuilder {
public static final SourceBuilder INSTANCE = new SourceBuilder();
private SourceBuilder() {
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().endObject();
}
}
} }

View File

@ -22,6 +22,7 @@ import java.io.IOException;
public class AlwaysTrueCondition extends Condition<Condition.Result> { public class AlwaysTrueCondition extends Condition<Condition.Result> {
public static final String TYPE = "always_true"; public static final String TYPE = "always_true";
public static final Result RESULT = new Result(TYPE, true) { public static final Result RESULT = new Result(TYPE, true) {
@Override @Override
@ -49,6 +50,10 @@ public class AlwaysTrueCondition extends Condition<Condition.Result> {
return builder.startObject().endObject(); return builder.startObject().endObject();
} }
@Override
public boolean equals(Object obj) {
return obj instanceof AlwaysTrueCondition;
}
public static class Parser extends AbstractComponent implements Condition.Parser<Result, AlwaysTrueCondition> { public static class Parser extends AbstractComponent implements Condition.Parser<Result, AlwaysTrueCondition> {
@ -87,4 +92,21 @@ public class AlwaysTrueCondition extends Condition<Condition.Result> {
} }
} }
public static class SourceBuilder implements Condition.SourceBuilder {
public static final SourceBuilder INSTANCE = new SourceBuilder();
private SourceBuilder() {
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().endObject();
}
}
} }

View File

@ -16,6 +16,7 @@ import org.elasticsearch.alerts.support.TemplateUtils;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy; import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.joda.time.DateTime;
@ -74,8 +75,9 @@ public class HistoryStore extends AbstractComponent {
public void update(FiredAlert firedAlert) throws HistoryException { public void update(FiredAlert firedAlert) throws HistoryException {
logger.debug("updating fired alert [{}]", firedAlert); logger.debug("updating fired alert [{}]", firedAlert);
try { try {
BytesReference bytes = XContentFactory.jsonBuilder().value(firedAlert).bytes();
IndexResponse response = client.prepareIndex(getAlertHistoryIndexNameForTime(firedAlert.scheduledTime()), ALERT_HISTORY_TYPE, firedAlert.id()) IndexResponse response = client.prepareIndex(getAlertHistoryIndexNameForTime(firedAlert.scheduledTime()), ALERT_HISTORY_TYPE, firedAlert.id())
.setSource(XContentFactory.jsonBuilder().value(firedAlert)) .setSource(bytes)
.setVersion(firedAlert.version()) .setVersion(firedAlert.version())
.get(); .get();
firedAlert.version(response.getVersion()); firedAlert.version(response.getVersion());

View File

@ -88,4 +88,10 @@ public abstract class Input<R extends Input.Result> implements ToXContent {
protected abstract XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException; protected abstract XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException;
} }
public static interface SourceBuilder extends ToXContent {
String type();
}
} }

View File

@ -0,0 +1,39 @@
/*
* 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.alerts.input;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.alerts.input.search.SearchInput;
import org.elasticsearch.alerts.input.simple.SimpleInput;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
public final class InputBuilders {
private InputBuilders() {
}
public static SearchInput.SourceBuilder searchInput(SearchRequest request) {
return new SearchInput.SourceBuilder(request);
}
public static SearchInput.SourceBuilder searchInput(SearchRequestBuilder builder) {
return searchInput(builder.request());
}
public static SimpleInput.SourceBuilder simpleInput() {
return simpleInput(new HashMap<String, Object>());
}
public static SimpleInput.SourceBuilder simpleInput(Map<String, Object> data) {
return new SimpleInput.SourceBuilder(data);
}
}

View File

@ -0,0 +1,128 @@
/*
* 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.alerts.input;
import org.elasticsearch.alerts.ExecutionContext;
import org.elasticsearch.alerts.Payload;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Map;
/**
*
*/
public class NoneInput extends Input<NoneInput.Result> {
public static final String TYPE = "none";
private static final Payload EMPTY_PAYLOAD = new Payload() {
@Override
public Map<String, Object> data() {
return ImmutableMap.of();
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().endObject();
}
};
public NoneInput(ESLogger logger) {
super(logger);
}
@Override
public String type() {
return TYPE;
}
@Override
public Result execute(ExecutionContext ctx) throws IOException {
return Result.INSTANCE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().endObject();
}
public static class Result extends Input.Result {
static final Result INSTANCE = new Result();
private Result() {
super(TYPE, EMPTY_PAYLOAD);
}
@Override
protected XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().endObject();
}
}
public static class Parser extends AbstractComponent implements Input.Parser<Result, NoneInput> {
private final NoneInput input;
public Parser(Settings settings) {
super(settings);
this.input = new NoneInput(logger);
}
@Override
public String type() {
return TYPE;
}
@Override
public NoneInput parse(XContentParser parser) throws IOException {
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
}
parser.nextToken();
if (parser.currentToken() != XContentParser.Token.END_OBJECT) {
}
return input;
}
@Override
public Result parseResult(XContentParser parser) throws IOException {
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
}
parser.nextToken();
if (parser.currentToken() != XContentParser.Token.END_OBJECT) {
}
return Result.INSTANCE;
}
}
public static class SourceBuilder implements Input.SourceBuilder {
public static final SourceBuilder INSTANCE = new SourceBuilder();
private SourceBuilder() {
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject().endObject();
}
}
}

View File

@ -13,6 +13,7 @@ import org.elasticsearch.alerts.Payload;
import org.elasticsearch.alerts.input.Input; import org.elasticsearch.alerts.input.Input;
import org.elasticsearch.alerts.input.InputException; import org.elasticsearch.alerts.input.InputException;
import org.elasticsearch.alerts.support.AlertUtils; import org.elasticsearch.alerts.support.AlertUtils;
import org.elasticsearch.alerts.support.SearchRequestEquivalence;
import org.elasticsearch.alerts.support.Variables; import org.elasticsearch.alerts.support.Variables;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy; import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy; import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
@ -44,6 +45,7 @@ import static org.elasticsearch.alerts.support.AlertsDateUtils.formatDate;
public class SearchInput extends Input<SearchInput.Result> { public class SearchInput extends Input<SearchInput.Result> {
public static final String TYPE = "search"; public static final String TYPE = "search";
public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.COUNT; public static final SearchType DEFAULT_SEARCH_TYPE = SearchType.COUNT;
private final SearchRequest searchRequest; private final SearchRequest searchRequest;
@ -86,12 +88,25 @@ public class SearchInput extends Input<SearchInput.Result> {
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(); return AlertUtils.writeSearchRequest(searchRequest, builder, params);
builder.field(Parser.REQUEST_FIELD.getPreferredName());
AlertUtils.writeSearchRequest(searchRequest, builder, params);
return builder.endObject();
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SearchInput that = (SearchInput) o;
if (!SearchRequestEquivalence.INSTANCE.equivalent(searchRequest, that.searchRequest)) return false;
return true;
}
@Override
public int hashCode() {
return SearchRequestEquivalence.INSTANCE.hash(searchRequest);
}
/** /**
* Creates a new search request applying the scheduledFireTime and fireTime to the original request * Creates a new search request applying the scheduledFireTime and fireTime to the original request
@ -136,11 +151,8 @@ public class SearchInput extends Input<SearchInput.Result> {
@Override @Override
protected XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException { protected XContentBuilder toXContentBody(XContentBuilder builder, Params params) throws IOException {
if (request != null) {
builder.field(Parser.REQUEST_FIELD.getPreferredName()); builder.field(Parser.REQUEST_FIELD.getPreferredName());
AlertUtils.writeSearchRequest(request, builder, params); return AlertUtils.writeSearchRequest(request, builder, params);
}
return builder;
} }
} }
@ -164,27 +176,10 @@ public class SearchInput extends Input<SearchInput.Result> {
@Override @Override
public SearchInput parse(XContentParser parser) throws IOException { public SearchInput parse(XContentParser parser) throws IOException {
SearchRequest request = null; SearchRequest request = AlertUtils.readSearchRequest(parser, DEFAULT_SEARCH_TYPE);
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 (REQUEST_FIELD.match(currentFieldName)) {
request = AlertUtils.readSearchRequest(parser, DEFAULT_SEARCH_TYPE);
} else {
throw new InputException("unable to parse [" + TYPE + "] input. unexpected field [" + currentFieldName + "]");
}
}
}
if (request == null) { if (request == null) {
throw new InputException("search request is missing or null for [" + TYPE + "] input"); throw new InputException("could not parse [search] input. search request is missing or null.");
} }
return new SearchInput(logger, scriptService, client, request); return new SearchInput(logger, scriptService, client, request);
} }
@ -222,4 +217,23 @@ public class SearchInput extends Input<SearchInput.Result> {
return new Result(TYPE, payload, request); return new Result(TYPE, payload, request);
} }
} }
public static class SourceBuilder implements Input.SourceBuilder {
private final SearchRequest request;
public SourceBuilder(SearchRequest request) {
this.request = request;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return AlertUtils.writeSearchRequest(request, builder, params);
}
}
} }

View File

@ -13,10 +13,14 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/** /**
* This class just defines a simple xcontent map as an input * This class just defines a simple xcontent map as an input
@ -24,6 +28,7 @@ import java.io.IOException;
public class SimpleInput extends Input<SimpleInput.Result> { public class SimpleInput extends Input<SimpleInput.Result> {
public static final String TYPE = "simple"; public static final String TYPE = "simple";
private final Payload payload; private final Payload payload;
public SimpleInput(ESLogger logger, Payload payload) { public SimpleInput(ESLogger logger, Payload payload) {
@ -48,6 +53,23 @@ public class SimpleInput extends Input<SimpleInput.Result> {
return builder.endObject(); return builder.endObject();
} }
@Override
public int hashCode() {
return Objects.hash(payload);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
final SimpleInput other = (SimpleInput) obj;
return Objects.equals(this.payload.data(), other.payload.data());
}
public static class Result extends Input.Result { public static class Result extends Input.Result {
public Result(String type, Payload payload) { public Result(String type, Payload payload) {
@ -122,4 +144,29 @@ public class SimpleInput extends Input<SimpleInput.Result> {
return new Result(TYPE, payload); return new Result(TYPE, payload);
} }
} }
public static class SourceBuilder implements Input.SourceBuilder {
private Map<String, Object> data;
public SourceBuilder(Map<String, Object> data) {
this.data = data;
}
public Input.SourceBuilder put(String key, Object value) {
data.put(key, value);
return this;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.map(data);
}
}
} }

View File

@ -40,7 +40,7 @@ public class RestPutAlertAction extends BaseRestHandler {
protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception { protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
PutAlertRequest putAlertRequest = new PutAlertRequest(); PutAlertRequest putAlertRequest = new PutAlertRequest();
putAlertRequest.setAlertName(request.param("name")); putAlertRequest.setAlertName(request.param("name"));
putAlertRequest.setAlertSource(request.content(), request.contentUnsafe()); putAlertRequest.source(request.content(), request.contentUnsafe());
alertsClient.putAlert(putAlertRequest, new RestBuilderListener<PutAlertResponse>(channel) { alertsClient.putAlert(putAlertRequest, new RestBuilderListener<PutAlertResponse>(channel) {
@Override @Override
public RestResponse buildResponse(PutAlertResponse response, XContentBuilder builder) throws Exception { public RestResponse buildResponse(PutAlertResponse response, XContentBuilder builder) throws Exception {

View File

@ -12,6 +12,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* *
@ -25,6 +26,10 @@ public class ScheduleRegistry {
this.parsers = ImmutableMap.copyOf(parsers); this.parsers = ImmutableMap.copyOf(parsers);
} }
public Set<String> types() {
return parsers.keySet();
}
public Schedule parse(XContentParser parser) throws IOException { public Schedule parse(XContentParser parser) throws IOException {
String type = null; String type = null;
XContentParser.Token token; XContentParser.Token token;

View File

@ -155,13 +155,19 @@ public final class AlertUtils {
/** /**
* Writes the searchRequest to the specified builder. * Writes the searchRequest to the specified builder.
*/ */
public static void writeSearchRequest(SearchRequest searchRequest, XContentBuilder builder, ToXContent.Params params) throws IOException { public static XContentBuilder writeSearchRequest(SearchRequest searchRequest, XContentBuilder builder, ToXContent.Params params) throws IOException {
if (searchRequest == null) { if (searchRequest == null) {
builder.nullValue(); builder.nullValue();
return; return builder;
} }
builder.startObject(); builder.startObject();
if (searchRequest.searchType() != null) {
builder.field("search_type", searchRequest.searchType().toString().toLowerCase(Locale.ENGLISH));
}
if (searchRequest.indices() != null) {
builder.array("indices", searchRequest.indices());
}
if (Strings.hasLength(searchRequest.source())) { if (Strings.hasLength(searchRequest.source())) {
XContentHelper.writeRawField("body", searchRequest.source(), builder, params); XContentHelper.writeRawField("body", searchRequest.source(), builder, params);
} }
@ -171,11 +177,6 @@ public final class AlertUtils {
if (searchRequest.templateType() != null) { if (searchRequest.templateType() != null) {
builder.field("template_type", searchRequest.templateType().name().toLowerCase(Locale.ROOT)); builder.field("template_type", searchRequest.templateType().name().toLowerCase(Locale.ROOT));
} }
builder.startArray("indices");
for (String index : searchRequest.indices()) {
builder.value(index);
}
builder.endArray();
if (searchRequest.indicesOptions() != DEFAULT_INDICES_OPTIONS) { if (searchRequest.indicesOptions() != DEFAULT_INDICES_OPTIONS) {
IndicesOptions options = searchRequest.indicesOptions(); IndicesOptions options = searchRequest.indicesOptions();
builder.startObject("indices_options"); builder.startObject("indices_options");
@ -194,10 +195,7 @@ public final class AlertUtils {
builder.field("allow_no_indices", options.allowNoIndices()); builder.field("allow_no_indices", options.allowNoIndices());
builder.endObject(); builder.endObject();
} }
if (searchRequest.searchType() != null) { return builder.endObject();
builder.field("search_type", searchRequest.searchType().toString().toLowerCase(Locale.ENGLISH));
}
builder.endObject();
} }
} }

View File

@ -91,7 +91,7 @@ public class Script implements ToXContent {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject() return builder.startObject()
.field(SCRIPT_FIELD.getPreferredName(), script) .field(SCRIPT_FIELD.getPreferredName(), script)
.field(TYPE_FIELD.getPreferredName(), type) .field(TYPE_FIELD.getPreferredName(), type.name().toLowerCase(Locale.ROOT))
.field(LANG_FIELD.getPreferredName(), lang) .field(LANG_FIELD.getPreferredName(), lang)
.field(PARAMS_FIELD.getPreferredName(), this.params) .field(PARAMS_FIELD.getPreferredName(), this.params)
.endObject(); .endObject();

View File

@ -0,0 +1,54 @@
/*
* 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.alerts.support;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.alerts.AlertsException;
import org.elasticsearch.common.base.Equivalence;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import java.io.IOException;
import java.util.Arrays;
/**
* The only true way today to compare search request object (outside of core) is to
* serialize it and compare the serialized output. this is heavy obviously, but luckily we
* don't compare search requests in normal runtime... we only do it in the tests. The is here basically
* due to the lack of equals/hashcode support in SearchRequest in core.
*/
public final class SearchRequestEquivalence extends Equivalence<SearchRequest> {
public static final SearchRequestEquivalence INSTANCE = new SearchRequestEquivalence();
private SearchRequestEquivalence() {
}
@Override
protected boolean doEquivalent(SearchRequest r1, SearchRequest r2) {
try {
BytesStreamOutput output1 = new BytesStreamOutput();
r1.writeTo(output1);
BytesReference bytes1 = output1.bytes();
BytesStreamOutput output2 = new BytesStreamOutput(bytes1.length());
r2.writeTo(output2);
BytesReference bytes2 = output2.bytes();
return Arrays.equals(bytes1.toBytes(), bytes2.toBytes());
} catch (IOException ioe) {
throw new AlertsException("could not compare search requests", ioe);
}
}
@Override
protected int doHash(SearchRequest request) {
try {
BytesStreamOutput output = new BytesStreamOutput();
request.writeTo(output);
return Arrays.hashCode(output.bytes().toBytes());
} catch (IOException ioe) {
throw new AlertsException("could not compute hashcode for search request", ioe);
}
}
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.alerts.support.template;
import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.yaml.YamlXContent; import org.elasticsearch.common.xcontent.yaml.YamlXContent;
import java.io.IOException; import java.io.IOException;
@ -18,6 +19,7 @@ import java.util.Map;
public class XContentTemplate implements Template { public class XContentTemplate implements Template {
public static XContentTemplate YAML = new XContentTemplate(YamlXContent.yamlXContent); public static XContentTemplate YAML = new XContentTemplate(YamlXContent.yamlXContent);
public static XContentTemplate JSON = new XContentTemplate(JsonXContent.jsonXContent);
private final XContent xContent; private final XContent xContent;

View File

@ -72,7 +72,11 @@ public class ChainTransform extends Transform {
@Override @Override
public void init(Injector injector) { public void init(Injector injector) {
this.registry = injector.getInstance(TransformRegistry.class); init(injector.getInstance(TransformRegistry.class));
}
public void init(TransformRegistry registry) {
this.registry = registry;
} }
@Override @Override
@ -106,8 +110,36 @@ public class ChainTransform extends Transform {
} }
return new ChainTransform(builder.build()); return new ChainTransform(builder.build());
} }
} }
public static class SourceBuilder implements Transform.SourceBuilder {
private final ImmutableList.Builder<Transform.SourceBuilder> builders = ImmutableList.builder();
@Override
public String type() {
return TYPE;
}
public SourceBuilder(Transform.SourceBuilder... builders) {
this.builders.add(builders);
}
public SourceBuilder add(Transform.SourceBuilder builder) {
builders.add(builder);
return this;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startArray();
for (Transform.SourceBuilder transBuilder : builders.build()) {
builder.startObject()
.field(TYPE, transBuilder)
.endObject();
}
return builder.endArray();
}
}
} }

View File

@ -26,12 +26,12 @@ public class ScriptTransform extends Transform {
public static final String TYPE = "script"; public static final String TYPE = "script";
private final Script script;
private final ScriptServiceProxy scriptService; private final ScriptServiceProxy scriptService;
private final Script script;
public ScriptTransform(Script script, ScriptServiceProxy scriptService) { public ScriptTransform(ScriptServiceProxy scriptService, Script script) {
this.script = script;
this.scriptService = scriptService; this.scriptService = scriptService;
this.script = script;
} }
@Override @Override
@ -83,7 +83,30 @@ public class ScriptTransform extends Transform {
} catch (Script.ParseException pe) { } catch (Script.ParseException pe) {
throw new AlertsSettingsException("could not parse [script] transform", pe); throw new AlertsSettingsException("could not parse [script] transform", pe);
} }
return new ScriptTransform(script, scriptService); return new ScriptTransform(scriptService, script);
}
}
public static class SourceBuilder implements Transform.SourceBuilder {
private final Script script;
public SourceBuilder(String script) {
this(new Script(script));
}
public SourceBuilder(Script script) {
this.script = script;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return script.toXContent(builder, params);
} }
} }
} }

View File

@ -48,6 +48,7 @@ public class SearchTransform extends Transform {
protected final ESLogger logger; protected final ESLogger logger;
protected final ScriptServiceProxy scriptService; protected final ScriptServiceProxy scriptService;
protected final ClientProxy client; protected final ClientProxy client;
protected final SearchRequest request; protected final SearchRequest request;
public SearchTransform(ESLogger logger, ScriptServiceProxy scriptService, ClientProxy client, SearchRequest request) { public SearchTransform(ESLogger logger, ScriptServiceProxy scriptService, ClientProxy client, SearchRequest request) {
@ -71,8 +72,7 @@ public class SearchTransform extends Transform {
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
AlertUtils.writeSearchRequest(request, builder, params); return AlertUtils.writeSearchRequest(request, builder, params);
return builder;
} }
public SearchRequest createRequest(SearchRequest requestPrototype, ExecutionContext ctx, Payload payload) throws IOException { public SearchRequest createRequest(SearchRequest requestPrototype, ExecutionContext ctx, Payload payload) throws IOException {
@ -160,4 +160,23 @@ public class SearchTransform extends Transform {
} }
} }
public static class SourceBuilder implements Transform.SourceBuilder {
private final SearchRequest request;
public SourceBuilder(SearchRequest request) {
this.request = request;
}
@Override
public String type() {
return TYPE;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return AlertUtils.writeSearchRequest(request, builder, params);
}
}
} }

View File

@ -69,7 +69,7 @@ public abstract class Transform implements ToXContent {
} }
} }
static interface Parser<T extends Transform> { public static interface Parser<T extends Transform> {
String type(); String type();
@ -77,4 +77,9 @@ public abstract class Transform implements ToXContent {
} }
public static interface SourceBuilder extends ToXContent {
String type();
}
} }

View File

@ -0,0 +1,39 @@
/*
* 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.alerts.transform;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.transform.ChainTransform;
import org.elasticsearch.alerts.transform.ScriptTransform;
import org.elasticsearch.alerts.transform.SearchTransform;
import org.elasticsearch.alerts.transform.Transform;
/**
*
*/
public final class TransformBuilders {
private TransformBuilders() {
}
public static SearchTransform.SourceBuilder searchTransform(SearchRequest request) {
return new SearchTransform.SourceBuilder(request);
}
public static ScriptTransform.SourceBuilder scriptTransform(String script) {
return new ScriptTransform.SourceBuilder(script);
}
public static ScriptTransform.SourceBuilder scriptTransform(Script script) {
return new ScriptTransform.SourceBuilder(script);
}
public static ChainTransform.SourceBuilder chainTransform(Transform.SourceBuilder... transforms) {
return new ChainTransform.SourceBuilder(transforms);
}
}

View File

@ -32,7 +32,7 @@ public class TransformRegistry {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
type = parser.currentName(); type = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT && type != null) { } else if (type != null) {
Transform.Parser transformParser = parsers.get(type); Transform.Parser transformParser = parsers.get(type);
if (transformParser == null) { if (transformParser == null) {
throw new AlertsSettingsException("unknown transform type [" + type + "]"); throw new AlertsSettingsException("unknown transform type [" + type + "]");

View File

@ -9,9 +9,12 @@ package org.elasticsearch.alerts.transport.actions.put;
import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.ValidateActions; import org.elasticsearch.action.ValidateActions;
import org.elasticsearch.action.support.master.MasterNodeOperationRequest; import org.elasticsearch.action.support.master.MasterNodeOperationRequest;
import org.elasticsearch.alerts.client.AlertSourceBuilder;
import org.elasticsearch.client.Requests;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException; import java.io.IOException;
@ -59,7 +62,14 @@ public class PutAlertRequest extends MasterNodeOperationRequest<PutAlertRequest>
/** /**
* Set the source of the alert * Set the source of the alert
*/ */
public void setAlertSource(BytesReference alertSource) { public void source(AlertSourceBuilder source) {
source(source.buildAsBytes(XContentType.JSON));
}
/**
* Set the source of the alert
*/
public void source(BytesReference alertSource) {
this.alertSource = alertSource; this.alertSource = alertSource;
this.alertSourceUnsafe = false; this.alertSourceUnsafe = false;
} }
@ -67,7 +77,7 @@ public class PutAlertRequest extends MasterNodeOperationRequest<PutAlertRequest>
/** /**
* Set the source of the alert with boolean to control source safety * Set the source of the alert with boolean to control source safety
*/ */
public void setAlertSource(BytesReference alertSource, boolean alertSourceUnsafe) { public void source(BytesReference alertSource, boolean alertSourceUnsafe) {
this.alertSource = alertSource; this.alertSource = alertSource;
this.alertSourceUnsafe = alertSourceUnsafe; this.alertSourceUnsafe = alertSourceUnsafe;
} }

View File

@ -7,6 +7,7 @@ package org.elasticsearch.alerts.transport.actions.put;
import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder; import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
import org.elasticsearch.alerts.client.AlertSourceBuilder;
import org.elasticsearch.alerts.client.AlertsClient; import org.elasticsearch.alerts.client.AlertsClient;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
@ -28,19 +29,26 @@ public class PutAlertRequestBuilder extends MasterNodeOperationRequestBuilder<Pu
/** /**
* @param alertName The alert name to be created * @param alertName The alert name to be created
*/ */
public PutAlertRequestBuilder setAlertName(String alertName){ public PutAlertRequestBuilder alertName(String alertName){
request.setAlertName(alertName); request.setAlertName(alertName);
return this; return this;
} }
/** /**
* @param alertSource the source of the alert to be created * @param source the source of the alert to be created
*/ */
public PutAlertRequestBuilder setAlertSource(BytesReference alertSource) { public PutAlertRequestBuilder source(BytesReference source) {
request.setAlertSource(alertSource); request.source(source);
return this; return this;
} }
/**
* @param source the source of the alert to be created
*/
public PutAlertRequestBuilder source(AlertSourceBuilder source) {
request.source(source);
return this;
}
@Override @Override
protected void doExecute(ActionListener<PutAlertResponse> listener) { protected void doExecute(ActionListener<PutAlertResponse> listener) {

View File

@ -1,75 +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.alerts;
import org.elasticsearch.alerts.actions.Action;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.junit.Test;
import java.io.IOException;
public class AlertSerializationTest extends AbstractAlertingTests {
@Test
public void testAlertSerialization() throws Exception {
Alert alert = createTestAlert("test-serialization");
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
final Alert.Parser alertParser =
internalTestCluster().getInstance(Alert.Parser.class, internalTestCluster().getMasterName());
Alert parsedAlert = alertParser.parse("test-serialization", true, jsonBuilder.bytes());
long parsedActionCount = 0;
long alertActionCount = 0;
for (Action action : parsedAlert.actions()) {
boolean found = false;
++parsedActionCount;
alertActionCount = 0;
for (Action action1 : alert.actions()) {
++alertActionCount;
if (action.type().equals(action1.type())) {
assertEqualByGeneratedXContent(action, action1);
found = true;
}
}
assertTrue(found);
}
assertEquals(parsedActionCount, alertActionCount);
assertEquals(parsedAlert,alert);
assertEquals(parsedAlert.status().version(), alert.status().version());
if (parsedAlert.status().lastExecuted() == null ) {
assertNull(alert.status().lastExecuted());
} else {
assertEquals(parsedAlert.status().lastExecuted().getMillis(), alert.status().lastExecuted().getMillis());
}
assertEqualByGeneratedXContent(parsedAlert.schedule(), alert.schedule());
assertEqualByGeneratedXContent(parsedAlert.condition(), alert.condition());
assertEquals(parsedAlert.throttlePeriod().getMillis(), alert.throttlePeriod().getMillis());
assertEquals(parsedAlert.status().ackStatus().state(), alert.status().ackStatus().state());
assertEquals(parsedAlert.metadata().get("foo"), "bar");
}
private void assertEqualByGeneratedXContent(ToXContent xCon1, ToXContent xCon2) throws IOException {
XContentBuilder builder1 = XContentFactory.jsonBuilder();
XContentBuilder builder2 = XContentFactory.jsonBuilder();
xCon1.toXContent(builder1, ToXContent.EMPTY_PARAMS);
xCon2.toXContent(builder2, ToXContent.EMPTY_PARAMS);
assertEquals(builder1.bytes().toUtf8(), builder2.bytes().toUtf8());
}
}

View File

@ -0,0 +1,281 @@
/*
* 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.alerts;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.alerts.actions.Action;
import org.elasticsearch.alerts.actions.ActionRegistry;
import org.elasticsearch.alerts.actions.Actions;
import org.elasticsearch.alerts.actions.email.EmailAction;
import org.elasticsearch.alerts.actions.email.service.Email;
import org.elasticsearch.alerts.actions.email.service.EmailService;
import org.elasticsearch.alerts.actions.email.service.Profile;
import org.elasticsearch.alerts.actions.index.IndexAction;
import org.elasticsearch.alerts.actions.webhook.HttpClient;
import org.elasticsearch.alerts.actions.webhook.WebhookAction;
import org.elasticsearch.alerts.condition.Condition;
import org.elasticsearch.alerts.condition.ConditionRegistry;
import org.elasticsearch.alerts.condition.script.ScriptCondition;
import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.alerts.input.Input;
import org.elasticsearch.alerts.input.InputRegistry;
import org.elasticsearch.alerts.input.search.SearchInput;
import org.elasticsearch.alerts.input.simple.SimpleInput;
import org.elasticsearch.alerts.scheduler.schedule.*;
import org.elasticsearch.alerts.scheduler.schedule.support.*;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.alerts.support.template.ScriptTemplate;
import org.elasticsearch.alerts.support.template.Template;
import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.elasticsearch.alerts.transform.*;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.netty.handler.codec.http.HttpMethod;
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.test.ElasticsearchTestCase;
import org.junit.Before;
import org.junit.Test;
import java.util.Map;
import static org.elasticsearch.alerts.test.AlertsTestUtils.matchAllRequest;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
public class AlertTests extends ElasticsearchTestCase {
private ScriptServiceProxy scriptService;
private ClientProxy client;
private HttpClient httpClient;
private EmailService emailService;
private Template.Parser templateParser;
private ESLogger logger;
private Settings settings = ImmutableSettings.EMPTY;
@Before
public void init() throws Exception {
scriptService = mock(ScriptServiceProxy.class);
client = mock(ClientProxy.class);
httpClient = mock(HttpClient.class);
emailService = mock(EmailService.class);
templateParser = new ScriptTemplate.Parser(settings, scriptService);
logger = Loggers.getLogger(AlertTests.class);
}
@Test @Repeat(iterations = 20)
public void testParser_SelfGenerated() throws Exception {
Schedule schedule = randomSchedule();
ScheduleRegistry scheduleRegistry = registry(schedule);
Input input = randomInput();
InputRegistry inputRegistry = registry(input);
Condition condition = randomCondition();
ConditionRegistry conditionRegistry = registry(condition);
Transform transform = randomTransform();
TransformRegistry transformRegistry = registry(transform);
Actions actions = randomActions();
ActionRegistry actionRegistry = registry(actions);
Map<String, Object> metadata = ImmutableMap.<String, Object>of("_key", "_val");
Alert.Status status = new Alert.Status();
TimeValue throttlePeriod = randomBoolean() ? null : TimeValue.timeValueSeconds(randomIntBetween(5, 10));
Alert alert = new Alert("_name", schedule, input, condition, transform, actions, metadata, throttlePeriod, status);
BytesReference bytes = XContentFactory.jsonBuilder().value(alert).bytes();
logger.info(bytes.toUtf8());
Alert.Parser alertParser = new Alert.Parser(settings, conditionRegistry, scheduleRegistry, transformRegistry, actionRegistry, inputRegistry);
boolean includeStatus = randomBoolean();
Alert parsedAlert = alertParser.parse("_name", includeStatus, bytes);
if (includeStatus) {
assertThat(parsedAlert.status(), equalTo(status));
}
assertThat(parsedAlert.schedule(), equalTo(schedule));
assertThat(parsedAlert.input(), equalTo(input));
assertThat(parsedAlert.condition(), equalTo(condition));
if (throttlePeriod != null) {
assertThat(parsedAlert.throttlePeriod().millis(), equalTo(throttlePeriod.millis()));
}
assertThat(parsedAlert.metadata(), equalTo(metadata));
assertThat(parsedAlert.actions(), equalTo(actions));
}
private static Schedule randomSchedule() {
String type = randomFrom(CronSchedule.TYPE, HourlySchedule.TYPE, DailySchedule.TYPE, WeeklySchedule.TYPE, MonthlySchedule.TYPE, YearlySchedule.TYPE, IntervalSchedule.TYPE);
switch (type) {
case CronSchedule.TYPE:
return new CronSchedule("0/5 * * * * ? *");
case HourlySchedule.TYPE:
return HourlySchedule.builder().minutes(30).build();
case DailySchedule.TYPE:
return DailySchedule.builder().atNoon().build();
case WeeklySchedule.TYPE:
return WeeklySchedule.builder().time(WeekTimes.builder().on(DayOfWeek.FRIDAY).atMidnight()).build();
case MonthlySchedule.TYPE:
return MonthlySchedule.builder().time(MonthTimes.builder().on(1).atNoon()).build();
case YearlySchedule.TYPE:
return YearlySchedule.builder().time(YearTimes.builder().in(Month.JANUARY).on(1).atMidnight()).build();
default:
return new IntervalSchedule(IntervalSchedule.Interval.seconds(5));
}
}
private static ScheduleRegistry registry(Schedule schedule) {
ImmutableMap.Builder<String, Schedule.Parser> parsers = ImmutableMap.builder();
switch (schedule.type()) {
case CronSchedule.TYPE:
parsers.put(CronSchedule.TYPE, new CronSchedule.Parser());
return new ScheduleRegistry(parsers.build());
case HourlySchedule.TYPE:
parsers.put(HourlySchedule.TYPE, new HourlySchedule.Parser());
return new ScheduleRegistry(parsers.build());
case DailySchedule.TYPE:
parsers.put(DailySchedule.TYPE, new DailySchedule.Parser());
return new ScheduleRegistry(parsers.build());
case WeeklySchedule.TYPE:
parsers.put(WeeklySchedule.TYPE, new WeeklySchedule.Parser());
return new ScheduleRegistry(parsers.build());
case MonthlySchedule.TYPE:
parsers.put(MonthlySchedule.TYPE, new MonthlySchedule.Parser());
return new ScheduleRegistry(parsers.build());
case YearlySchedule.TYPE:
parsers.put(YearlySchedule.TYPE, new YearlySchedule.Parser());
return new ScheduleRegistry(parsers.build());
case IntervalSchedule.TYPE:
parsers.put(IntervalSchedule.TYPE, new IntervalSchedule.Parser());
return new ScheduleRegistry(parsers.build());
default:
throw new IllegalArgumentException("unknown schedule [" + schedule + "]");
}
}
private Input randomInput() {
String type = randomFrom(SearchInput.TYPE, SimpleInput.TYPE);
switch (type) {
case SearchInput.TYPE:
return new SearchInput(logger, scriptService, client, AlertsTestUtils.newInputSearchRequest("idx"));
default:
return new SimpleInput(logger, new Payload.Simple(ImmutableMap.<String, Object>builder().put("_key", "_val").build()));
}
}
private InputRegistry registry(Input input) {
ImmutableMap.Builder<String, Input.Parser> parsers = ImmutableMap.builder();
switch (input.type()) {
case SearchInput.TYPE:
parsers.put(SearchInput.TYPE, new SearchInput.Parser(settings, scriptService, client));
return new InputRegistry(parsers.build());
default:
parsers.put(SimpleInput.TYPE, new SimpleInput.Parser(settings));
return new InputRegistry(parsers.build());
}
}
private Condition randomCondition() {
String type = randomFrom(ScriptCondition.TYPE, AlwaysTrueCondition.TYPE);
switch (type) {
case ScriptCondition.TYPE:
return new ScriptCondition(logger, scriptService, new Script("_script"));
default:
return new AlwaysTrueCondition(logger);
}
}
private ConditionRegistry registry(Condition condition) {
ImmutableMap.Builder<String, Condition.Parser> parsers = ImmutableMap.builder();
switch (condition.type()) {
case ScriptCondition.TYPE:
parsers.put(ScriptCondition.TYPE, new ScriptCondition.Parser(settings, scriptService));
return new ConditionRegistry(parsers.build());
default:
parsers.put(AlwaysTrueCondition.TYPE, new AlwaysTrueCondition.Parser(settings));
return new ConditionRegistry(parsers.build());
}
}
private Transform randomTransform() {
String type = randomFrom(ScriptTransform.TYPE, SearchTransform.TYPE, ChainTransform.TYPE);
switch (type) {
case ScriptTransform.TYPE:
return new ScriptTransform(scriptService, new Script("_script"));
case SearchTransform.TYPE:
return new SearchTransform(logger, scriptService, client, matchAllRequest());
default: // chain
return new ChainTransform(ImmutableList.of(
new SearchTransform(logger, scriptService, client, matchAllRequest()),
new ScriptTransform(scriptService, new Script("_script"))));
}
}
private TransformRegistry registry(Transform transform) {
ImmutableMap.Builder<String, Transform.Parser> parsers = ImmutableMap.builder();
switch (transform.type()) {
case ScriptTransform.TYPE:
parsers.put(ScriptTransform.TYPE, new ScriptTransform.Parser(scriptService));
return new TransformRegistry(parsers.build());
case SearchTransform.TYPE:
parsers.put(SearchTransform.TYPE, new SearchTransform.Parser(settings, scriptService, client));
return new TransformRegistry(parsers.build());
default:
ChainTransform.Parser parser = new ChainTransform.Parser();
parsers.put(ChainTransform.TYPE, parser);
parsers.put(ScriptTransform.TYPE, new ScriptTransform.Parser(scriptService));
parsers.put(SearchTransform.TYPE, new SearchTransform.Parser(settings, scriptService, client));
TransformRegistry registry = new TransformRegistry(parsers.build());
parser.init(registry);
return registry;
}
}
private Actions randomActions() {
ImmutableList.Builder<Action> list = ImmutableList.builder();
if (randomBoolean()) {
list.add(new EmailAction(logger, emailService, Email.builder().id("prototype").build(), null, Profile.STANDARD, null, null, null, null, randomBoolean()));
}
if (randomBoolean()) {
list.add(new IndexAction(logger, client, "_index", "_type"));
}
if (randomBoolean()) {
list.add(new WebhookAction(logger, httpClient, randomFrom(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT), new ScriptTemplate(scriptService, "_url"), null));
}
return new Actions(list.build());
}
private ActionRegistry registry(Actions actions) {
ImmutableMap.Builder<String, Action.Parser> parsers = ImmutableMap.builder();
for (Action action : actions) {
switch (action.type()) {
case EmailAction.TYPE:
parsers.put(EmailAction.TYPE, new EmailAction.Parser(settings, emailService, templateParser));
break;
case IndexAction.TYPE:
parsers.put(IndexAction.TYPE, new IndexAction.Parser(settings, client));
break;
case WebhookAction.TYPE:
parsers.put(WebhookAction.TYPE, new WebhookAction.Parser(settings, templateParser, httpClient));
break;
}
}
return new ActionRegistry(parsers.build());
}
}

View File

@ -1,101 +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.alerts.actions;
import org.elasticsearch.alerts.AbstractAlertingTests;
import org.elasticsearch.alerts.Alert;
import org.elasticsearch.alerts.actions.index.IndexAction;
import org.elasticsearch.alerts.condition.Condition;
import org.elasticsearch.alerts.condition.script.ScriptCondition;
import org.elasticsearch.alerts.input.Input;
import org.elasticsearch.alerts.input.search.SearchInput;
import org.elasticsearch.alerts.scheduler.schedule.CronSchedule;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.transform.SearchTransform;
import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertRequest;
import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse;
import org.elasticsearch.alerts.transport.actions.get.GetAlertRequest;
import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse;
import org.elasticsearch.alerts.transport.actions.put.PutAlertRequest;
import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse;
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.script.ScriptService;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
/**
*/
public class ActionsTest extends AbstractAlertingTests {
@Test
public void testAlertActions() throws Exception {
//TODO: Consider deleting this test or making it do something useful
createIndex("my-index");
ensureGreen("my-index");
client().preparePutIndexedScript()
.setScriptLang("mustache")
.setId("query")
.setSource(jsonBuilder().startObject().startObject("template").startObject("match_all").endObject().endObject().endObject())
.get();
ensureAlertingStarted();
final Action alertAction = new IndexAction(logger, ClientProxy.of(client()), "testindex", "testtype");
final List<Action> actionList = new ArrayList<>();
actionList.add(alertAction);
Input alertInput = new SearchInput(logger, scriptService(), ClientProxy.of(client()),
createConditionSearchRequest());
Condition alertCondition = new ScriptCondition(logger, scriptService(), new Script("return true"));
Alert alert = new Alert(
"my-first-alert",
new CronSchedule("0/5 * * * * ? *"),
alertInput,
alertCondition,
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), createConditionSearchRequest()),
new Actions(actionList), null, new Alert.Status(), new TimeValue(0)
);
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
PutAlertRequest alertRequest = alertClient().preparePutAlert().setAlertName("my-first-alert").setAlertSource(jsonBuilder.bytes()).request();
PutAlertResponse alertsResponse = alertClient().putAlert(alertRequest).actionGet();
assertNotNull(alertsResponse.indexResponse());
assertTrue(alertsResponse.indexResponse().isCreated());
GetAlertRequest getAlertRequest = new GetAlertRequest(alert.name());
GetAlertResponse getAlertResponse = alertClient().getAlert(getAlertRequest).actionGet();
assertTrue(getAlertResponse.getResponse().isExists());
assertEquals(((Map<String,Object>)getAlertResponse.getResponse().getSourceAsMap().get("schedule")).get("cron").toString(), "0/5 * * * * ? *");
DeleteAlertRequest deleteAlertRequest = new DeleteAlertRequest(alert.name());
DeleteAlertResponse deleteAlertResponse = alertClient().deleteAlert(deleteAlertRequest).actionGet();
assertNotNull(deleteAlertResponse.deleteResponse());
assertTrue(deleteAlertResponse.deleteResponse().isFound());
getAlertResponse = alertClient().getAlert(getAlertRequest).actionGet();
assertFalse(getAlertResponse.getResponse().isExists());
}
}

View File

@ -18,7 +18,8 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
/** /**
*/ */
public class TestAlwaysTrueCondition extends ElasticsearchTestCase { public class AlwaysFalseConditionTests extends ElasticsearchTestCase {
@Test @Test
public void testExecute() throws Exception { public void testExecute() throws Exception {
@ -34,6 +35,7 @@ public class TestAlwaysTrueCondition extends ElasticsearchTestCase {
builder.endObject(); builder.endObject();
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
xp.nextToken(); xp.nextToken();
Condition alwaysTrue = p.parse(xp); Condition alwaysTrue = p.parse(xp);
assertTrue(alwaysTrue.execute(null).met()); assertTrue(alwaysTrue.execute(null).met());
} }
@ -47,6 +49,7 @@ public class TestAlwaysTrueCondition extends ElasticsearchTestCase {
builder.endObject(); builder.endObject();
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
xp.nextToken(); xp.nextToken();
p.parse(xp); p.parse(xp);
fail("expected a condition exception trying to parse an invalid condition XContent, [" fail("expected a condition exception trying to parse an invalid condition XContent, ["
+ AlwaysTrueCondition.TYPE + "] condition should not parse with a body"); + AlwaysTrueCondition.TYPE + "] condition should not parse with a body");
@ -80,5 +83,4 @@ public class TestAlwaysTrueCondition extends ElasticsearchTestCase {
fail("expected a condition exception trying to parse an invalid condition result XContent, [" fail("expected a condition exception trying to parse an invalid condition result XContent, ["
+ AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field"); + AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field");
} }
} }

View File

@ -18,8 +18,7 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
/** /**
*/ */
public class TestAlwaysFalseCondition extends ElasticsearchTestCase { public class AlwaysTrueConditionTests extends ElasticsearchTestCase {
@Test @Test
public void testExecute() throws Exception { public void testExecute() throws Exception {
@ -35,7 +34,6 @@ public class TestAlwaysFalseCondition extends ElasticsearchTestCase {
builder.endObject(); builder.endObject();
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
xp.nextToken(); xp.nextToken();
Condition alwaysTrue = p.parse(xp); Condition alwaysTrue = p.parse(xp);
assertTrue(alwaysTrue.execute(null).met()); assertTrue(alwaysTrue.execute(null).met());
} }
@ -49,7 +47,6 @@ public class TestAlwaysFalseCondition extends ElasticsearchTestCase {
builder.endObject(); builder.endObject();
XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes()); XContentParser xp = XContentFactory.xContent(builder.bytes()).createParser(builder.bytes());
xp.nextToken(); xp.nextToken();
p.parse(xp); p.parse(xp);
fail("expected a condition exception trying to parse an invalid condition XContent, [" fail("expected a condition exception trying to parse an invalid condition XContent, ["
+ AlwaysTrueCondition.TYPE + "] condition should not parse with a body"); + AlwaysTrueCondition.TYPE + "] condition should not parse with a body");
@ -83,4 +80,5 @@ public class TestAlwaysFalseCondition extends ElasticsearchTestCase {
fail("expected a condition exception trying to parse an invalid condition result XContent, [" fail("expected a condition exception trying to parse an invalid condition result XContent, ["
+ AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field"); + AlwaysTrueCondition.TYPE + "] condition result should not parse with a [met] field");
} }
} }

View File

@ -13,6 +13,8 @@ import org.elasticsearch.alerts.condition.simple.AlwaysFalseCondition;
import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition; import org.elasticsearch.alerts.condition.simple.AlwaysTrueCondition;
import org.elasticsearch.alerts.input.Input; import org.elasticsearch.alerts.input.Input;
import org.elasticsearch.alerts.input.simple.SimpleInput; import org.elasticsearch.alerts.input.simple.SimpleInput;
import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.elasticsearch.alerts.throttle.Throttler; import org.elasticsearch.alerts.throttle.Throttler;
import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
@ -22,12 +24,12 @@ import org.junit.Test;
/** /**
*/ */
public class FiredAlertTest extends AbstractAlertingTests { public class FiredAlertTests extends AbstractAlertsIntegrationTests {
@Test @Test
public void testParser() throws Exception { public void testParser() throws Exception {
Alert alert = createTestAlert("fired_test"); Alert alert = AlertsTestUtils.createTestAlert("fired_test", scriptService(), httpClient(), noopEmailService(), logger);
FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime()); FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime());
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
firedAlert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); firedAlert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
@ -43,7 +45,7 @@ public class FiredAlertTest extends AbstractAlertingTests {
@Test @Test
public void testParser_WithSealedFiredAlert() throws Exception { public void testParser_WithSealedFiredAlert() throws Exception {
Alert alert = createTestAlert("fired_test"); Alert alert = AlertsTestUtils.createTestAlert("fired_test", scriptService(), httpClient(), noopEmailService(), logger);
FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime()); FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime());
ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime()); ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime());
ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah")); ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah"));
@ -65,7 +67,7 @@ public class FiredAlertTest extends AbstractAlertingTests {
@Test @Test
public void testParser_WithSealedFiredAlert_WithScriptSearchCondition() throws Exception { public void testParser_WithSealedFiredAlert_WithScriptSearchCondition() throws Exception {
Alert alert = createTestAlert("fired_test"); Alert alert = AlertsTestUtils.createTestAlert("fired_test", scriptService(), httpClient(), noopEmailService(), logger);
FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime()); FiredAlert firedAlert = new FiredAlert(alert, new DateTime(), new DateTime());
ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime()); ExecutionContext ctx = new ExecutionContext(firedAlert.id(), alert, new DateTime(), new DateTime());
ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah")); ctx.onActionResult(new EmailAction.Result.Failure("failed to send because blah"));

View File

@ -14,10 +14,10 @@ import static org.hamcrest.core.IsEqual.equalTo;
/** /**
*/ */
public class ActionHistoryIndexNameTest extends ElasticsearchTestCase { public class HistoryStoreTests extends ElasticsearchTestCase {
@Test @Test
public void testActionHistoryNameTest() { public void testIndexNameGeneration() {
assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(0, DateTimeZone.UTC)), equalTo(".alert_history_1970-01-01")); assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(0, DateTimeZone.UTC)), equalTo(".alert_history_1970-01-01"));
assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(100000000000L, DateTimeZone.UTC)), equalTo(".alert_history_1973-03-03")); assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(100000000000L, DateTimeZone.UTC)), equalTo(".alert_history_1973-03-03"));
assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(1416582852000L, DateTimeZone.UTC)), equalTo(".alert_history_2014-11-21")); assertThat(HistoryStore.getAlertHistoryIndexNameForTime(new DateTime(1416582852000L, DateTimeZone.UTC)), equalTo(".alert_history_2014-11-21"));

View File

@ -19,6 +19,8 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder;
@ -70,27 +72,21 @@ public class SearchInputTests extends ElasticsearchIntegrationTest {
@Test @Test
public void testParser_Valid() throws Exception { public void testParser_Valid() throws Exception {
SearchSourceBuilder searchSourceBuilder = searchSource().query( SearchRequest request = client().prepareSearch()
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}"))
);
SearchRequest request = client()
.prepareSearch()
.setSearchType(SearchInput.DEFAULT_SEARCH_TYPE) .setSearchType(SearchInput.DEFAULT_SEARCH_TYPE)
.request() .request()
.source(searchSourceBuilder); .source(searchSource()
.query(filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{" + Variables.SCHEDULED_FIRE_TIME + "}}||-30s").to("{{" + Variables.SCHEDULED_FIRE_TIME + "}}"))));
XContentBuilder jsonBuilder = jsonBuilder(); XContentBuilder builder = AlertUtils.writeSearchRequest(request, jsonBuilder(), ToXContent.EMPTY_PARAMS);
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken();
jsonBuilder.startObject(); Input.Parser searchInputParser = new SearchInput.Parser(ImmutableSettings.EMPTY,
jsonBuilder.field(SearchInput.Parser.REQUEST_FIELD.getPreferredName());
AlertUtils.writeSearchRequest(request, jsonBuilder, ToXContent.EMPTY_PARAMS);
jsonBuilder.endObject();
Input.Parser searchInputParser = new SearchInput.Parser(ImmutableSettings.settingsBuilder().build(),
ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class)), ScriptServiceProxy.of(internalCluster().getInstance(ScriptService.class)),
ClientProxy.of(client())); ClientProxy.of(client()));
Input searchInput = searchInputParser.parse(XContentFactory.xContent(jsonBuilder.bytes()).createParser(jsonBuilder.bytes())); Input searchInput = searchInputParser.parse(parser);
assertEquals(SearchInput.TYPE, searchInput.type()); assertEquals(SearchInput.TYPE, searchInput.type());
} }

View File

@ -97,7 +97,6 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
.field(DailySchedule.TYPE, daily) .field(DailySchedule.TYPE, daily)
.endObject(); .endObject();
BytesReference bytes = builder.bytes(); BytesReference bytes = builder.bytes();
System.out.println(bytes.toUtf8());
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken(); parser.nextToken();
Schedule schedule = registry.parse(parser); Schedule schedule = registry.parse(parser);
@ -114,7 +113,6 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
.field(WeeklySchedule.TYPE, weekly) .field(WeeklySchedule.TYPE, weekly)
.endObject(); .endObject();
BytesReference bytes = builder.bytes(); BytesReference bytes = builder.bytes();
System.out.println(bytes.toUtf8());
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken(); parser.nextToken();
Schedule schedule = registry.parse(parser); Schedule schedule = registry.parse(parser);
@ -131,7 +129,6 @@ public class ScheduleRegistryTests extends ScheduleTestCase {
.field(MonthlySchedule.TYPE, monthly) .field(MonthlySchedule.TYPE, monthly)
.endObject(); .endObject();
BytesReference bytes = builder.bytes(); BytesReference bytes = builder.bytes();
System.out.println(bytes.toUtf8());
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken(); parser.nextToken();
Schedule schedule = registry.parse(parser); Schedule schedule = registry.parse(parser);

View File

@ -116,7 +116,6 @@ public class ScriptTemplateTests extends ElasticsearchTestCase {
XContentBuilder builder = jsonBuilder().value(template); XContentBuilder builder = jsonBuilder().value(template);
BytesReference bytes = builder.bytes(); BytesReference bytes = builder.bytes();
System.out.println(bytes.toUtf8());
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken(); parser.nextToken();
ScriptTemplate parsed = templateParser.parse(parser); ScriptTemplate parsed = templateParser.parse(parser);

View File

@ -3,33 +3,24 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts; package org.elasticsearch.alerts.test;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.alerts.actions.Action; import org.elasticsearch.alerts.AlertsPlugin;
import org.elasticsearch.alerts.actions.Actions; import org.elasticsearch.alerts.AlertsService;
import org.elasticsearch.alerts.actions.email.EmailAction;
import org.elasticsearch.alerts.actions.email.service.Authentication; import org.elasticsearch.alerts.actions.email.service.Authentication;
import org.elasticsearch.alerts.actions.email.service.Email; import org.elasticsearch.alerts.actions.email.service.Email;
import org.elasticsearch.alerts.actions.email.service.EmailService; import org.elasticsearch.alerts.actions.email.service.EmailService;
import org.elasticsearch.alerts.actions.email.service.Profile; import org.elasticsearch.alerts.actions.email.service.Profile;
import org.elasticsearch.alerts.actions.webhook.HttpClient; import org.elasticsearch.alerts.actions.webhook.HttpClient;
import org.elasticsearch.alerts.actions.webhook.WebhookAction;
import org.elasticsearch.alerts.client.AlertsClient; import org.elasticsearch.alerts.client.AlertsClient;
import org.elasticsearch.alerts.condition.script.ScriptCondition;
import org.elasticsearch.alerts.history.FiredAlert; import org.elasticsearch.alerts.history.FiredAlert;
import org.elasticsearch.alerts.history.HistoryStore; import org.elasticsearch.alerts.history.HistoryStore;
import org.elasticsearch.alerts.input.search.SearchInput;
import org.elasticsearch.alerts.scheduler.schedule.CronSchedule;
import org.elasticsearch.alerts.support.AlertUtils; import org.elasticsearch.alerts.support.AlertUtils;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy; import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.alerts.support.template.ScriptTemplate;
import org.elasticsearch.alerts.support.template.Template; import org.elasticsearch.alerts.support.template.Template;
import org.elasticsearch.alerts.transform.SearchTransform;
import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterState;
@ -37,36 +28,34 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.hppc.cursors.ObjectObjectCursor; import org.elasticsearch.common.hppc.cursors.ObjectObjectCursor;
import org.elasticsearch.common.netty.handler.codec.http.HttpMethod;
import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.InternalTestCluster;
import org.elasticsearch.test.TestCluster; import org.elasticsearch.test.TestCluster;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import javax.mail.internet.AddressException;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.*; import java.util.*;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.*; import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.SUITE;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.Is.is; import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.IsNot.not;
/** /**
*/ */
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false) @ClusterScope(scope = SUITE, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false)
public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest { public abstract class AbstractAlertsIntegrationTests extends ElasticsearchIntegrationTest {
@Override @Override
protected Settings nodeSettings(int nodeOrdinal) { protected Settings nodeSettings(int nodeOrdinal) {
@ -129,10 +118,8 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
builder.startObject("input"); builder.startObject("input");
{ {
builder.startObject("search"); builder.field("search");
builder.field("request");
AlertUtils.writeSearchRequest(conditionRequest, builder, ToXContent.EMPTY_PARAMS); AlertUtils.writeSearchRequest(conditionRequest, builder, ToXContent.EMPTY_PARAMS);
builder.endObject();
} }
builder.endObject(); builder.endObject();
@ -163,56 +150,6 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
return builder.bytes(); return builder.bytes();
} }
public static SearchRequest createConditionSearchRequest(String... indices) {
SearchRequest request = new SearchRequest(indices);
request.indicesOptions(AlertUtils.DEFAULT_INDICES_OPTIONS);
request.searchType(SearchInput.DEFAULT_SEARCH_TYPE);
return request;
}
protected Alert createTestAlert(String alertName) throws AddressException {
SearchRequest conditionRequest = createConditionSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery()));
SearchRequest transformRequest = createConditionSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery()));
transformRequest.searchType(SearchTransform.DEFAULT_SEARCH_TYPE);
conditionRequest.searchType(SearchInput.DEFAULT_SEARCH_TYPE);
List<Action> actions = new ArrayList<>();
Template url = new ScriptTemplate(scriptService(), "http://localhost/foobarbaz/{{alert_name}}");
Template body = new ScriptTemplate(scriptService(), "{{alert_name}} executed with {{response.hits.total}} hits");
actions.add(new WebhookAction(logger, httpClient(), HttpMethod.GET, url, body));
Email.Address from = new Email.Address("from@test.com");
List<Email.Address> emailAddressList = new ArrayList<>();
emailAddressList.add(new Email.Address("to@test.com"));
Email.AddressList to = new Email.AddressList(emailAddressList);
Email.Builder emailBuilder = Email.builder().id("prototype");
emailBuilder.from(from);
emailBuilder.to(to);
EmailAction emailAction = new EmailAction(logger, noopEmailService(), emailBuilder.build(),
new Authentication("testname", "testpassword"), Profile.STANDARD, "testaccount", body, body, null, true);
actions.add(emailAction);
Map<String, Object> metadata = new LinkedHashMap<>();
metadata.put("foo", "bar");
return new Alert(
alertName,
new CronSchedule("0/5 * * * * ? *"),
new SearchInput(logger, scriptService(), ClientProxy.of(client()),
conditionRequest),
new ScriptCondition(logger, scriptService(), new Script("return true")),
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), transformRequest), new Actions(actions), metadata, new Alert.Status(), new TimeValue(0)
);
}
protected AlertsClient alertClient() { protected AlertsClient alertClient() {
return internalTestCluster().getInstance(AlertsClient.class); return internalTestCluster().getInstance(AlertsClient.class);
} }

View File

@ -0,0 +1,107 @@
/*
* 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.alerts.test;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.alerts.Alert;
import org.elasticsearch.alerts.actions.Action;
import org.elasticsearch.alerts.actions.Actions;
import org.elasticsearch.alerts.actions.email.EmailAction;
import org.elasticsearch.alerts.actions.email.service.Authentication;
import org.elasticsearch.alerts.actions.email.service.Email;
import org.elasticsearch.alerts.actions.email.service.EmailService;
import org.elasticsearch.alerts.actions.email.service.Profile;
import org.elasticsearch.alerts.actions.webhook.HttpClient;
import org.elasticsearch.alerts.actions.webhook.WebhookAction;
import org.elasticsearch.alerts.condition.script.ScriptCondition;
import org.elasticsearch.alerts.input.search.SearchInput;
import org.elasticsearch.alerts.scheduler.schedule.CronSchedule;
import org.elasticsearch.alerts.support.AlertUtils;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.support.init.proxy.ScriptServiceProxy;
import org.elasticsearch.alerts.support.template.ScriptTemplate;
import org.elasticsearch.alerts.support.template.Template;
import org.elasticsearch.alerts.transform.SearchTransform;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.netty.handler.codec.http.HttpMethod;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import javax.mail.internet.AddressException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
/**
*
*/
public final class AlertsTestUtils {
private AlertsTestUtils() {
}
public static SearchRequest newInputSearchRequest(String... indices) {
SearchRequest request = new SearchRequest(indices);
request.indicesOptions(AlertUtils.DEFAULT_INDICES_OPTIONS);
request.searchType(SearchInput.DEFAULT_SEARCH_TYPE);
return request;
}
public static SearchRequest matchAllRequest() {
return new SearchRequest().source(SearchSourceBuilder.searchSource().query(matchAllQuery()));
}
public static Alert createTestAlert(String alertName, ScriptServiceProxy scriptService, HttpClient httpClient, EmailService emailService, ESLogger logger) throws AddressException {
SearchRequest conditionRequest = newInputSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery()));
SearchRequest transformRequest = newInputSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery()));
transformRequest.searchType(SearchTransform.DEFAULT_SEARCH_TYPE);
conditionRequest.searchType(SearchInput.DEFAULT_SEARCH_TYPE);
List<Action> actions = new ArrayList<>();
Template url = new ScriptTemplate(scriptService, "http://localhost/foobarbaz/{{alert_name}}");
Template body = new ScriptTemplate(scriptService, "{{alert_name}} executed with {{response.hits.total}} hits");
actions.add(new WebhookAction(logger, httpClient, HttpMethod.GET, url, body));
Email.Address from = new Email.Address("from@test.com");
List<Email.Address> emailAddressList = new ArrayList<>();
emailAddressList.add(new Email.Address("to@test.com"));
Email.AddressList to = new Email.AddressList(emailAddressList);
Email.Builder emailBuilder = Email.builder().id("prototype");
emailBuilder.from(from);
emailBuilder.to(to);
EmailAction emailAction = new EmailAction(logger, emailService, emailBuilder.build(),
new Authentication("testname", "testpassword"), Profile.STANDARD, "testaccount", body, body, null, true);
actions.add(emailAction);
Map<String, Object> metadata = new LinkedHashMap<>();
metadata.put("foo", "bar");
return new Alert(
alertName,
new CronSchedule("0/5 * * * * ? *"),
new SearchInput(logger, scriptService, ClientProxy.of(ElasticsearchIntegrationTest.client()),
conditionRequest),
new ScriptCondition(logger, scriptService, new Script("return true")),
new SearchTransform(logger, scriptService, ClientProxy.of(ElasticsearchIntegrationTest.client()), transformRequest),
new Actions(actions),
metadata,
new TimeValue(0),
new Alert.Status());
}
}

View File

@ -3,11 +3,12 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts; package org.elasticsearch.alerts.test.integration;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.alerts.history.HistoryStore; import org.elasticsearch.alerts.history.HistoryStore;
import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
@ -15,14 +16,19 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder;
import static org.elasticsearch.alerts.condition.ConditionBuilders.scriptCondition;
import static org.elasticsearch.alerts.input.InputBuilders.searchInput;
import static org.elasticsearch.alerts.scheduler.schedule.Schedules.cron;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
/** /**
*
*/ */
public class AlertMetadataTest extends AbstractAlertingTests { public class AlertMetadataTests extends AbstractAlertsIntegrationTests {
@Test @Test
public void testAlertMetadata() throws Exception { public void testAlertMetadata() throws Exception {
@ -35,9 +41,12 @@ public class AlertMetadataTest extends AbstractAlertingTests {
metaList.add("test"); metaList.add("test");
metadata.put("baz", metaList); metadata.put("baz", metaList);
SearchRequest conditionRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery()));
alertClient().preparePutAlert("1") alertClient().preparePutAlert("1")
.setAlertSource(createAlertSource("0/5 * * * * ? *", conditionRequest, "hits.total == 1", metadata)) .source(alertSourceBuilder()
.schedule(cron("0/5 * * * * ? *"))
.input(searchInput(AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(matchAllQuery()))))
.condition(scriptCondition("hits.total == 1"))
.metadata(metadata))
.get(); .get();
// Wait for a no action entry to be added. (the condition search request will not match, because there are no docs in my-index) // Wait for a no action entry to be added. (the condition search request will not match, because there are no docs in my-index)
assertAlertWithNoActionNeeded("1", 1); assertAlertWithNoActionNeeded("1", 1);

View File

@ -3,42 +3,47 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts.actions; package org.elasticsearch.alerts.test.integration;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.alerts.*; import org.elasticsearch.alerts.*;
import org.elasticsearch.alerts.client.AlertsClient; import org.elasticsearch.alerts.client.AlertsClient;
import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsRequest; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsRequest;
import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.junit.Test; import org.junit.Test;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsEqual.equalTo;
/** /**
*/ */
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false) @ClusterScope(scope = TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false)
public class AlertStatsTests extends AbstractAlertingTests { public class AlertStatsTests extends AbstractAlertsIntegrationTests {
@Test @Test
public void testStartedStats() throws Exception { public void testStartedStats() throws Exception {
AlertsStatsRequest alertsStatsRequest = alertClient().prepareAlertsStats().request(); AlertsStatsRequest alertsStatsRequest = alertClient().prepareAlertsStats().request();
AlertsStatsResponse response = alertClient().alertsStats(alertsStatsRequest).actionGet(); AlertsStatsResponse response = alertClient().alertsStats(alertsStatsRequest).actionGet();
assertTrue(response.isAlertActionManagerStarted()); assertThat(response.isAlertActionManagerStarted(), is(true));
assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); assertThat(response.getAlertManagerStarted(), is(AlertsService.State.STARTED));
assertThat(response.getAlertActionManagerQueueSize(), equalTo(0L)); assertThat(response.getAlertActionManagerQueueSize(), is(0L));
assertThat(response.getNumberOfRegisteredAlerts(), equalTo(0L)); assertThat(response.getNumberOfRegisteredAlerts(), is(0L));
assertThat(response.getAlertActionManagerLargestQueueSize(), equalTo(0L)); assertThat(response.getAlertActionManagerLargestQueueSize(), is(0L));
assertThat(response.getVersion(), equalTo(AlertsVersion.CURRENT)); assertThat(response.getVersion(), is(AlertsVersion.CURRENT));
assertThat(response.getBuild(), equalTo(AlertsBuild.CURRENT)); assertThat(response.getBuild(), is(AlertsBuild.CURRENT));
} }
@Test @Test
@ -48,13 +53,13 @@ public class AlertStatsTests extends AbstractAlertingTests {
AlertsStatsRequest alertsStatsRequest = alertsClient.prepareAlertsStats().request(); AlertsStatsRequest alertsStatsRequest = alertsClient.prepareAlertsStats().request();
AlertsStatsResponse response = alertsClient.alertsStats(alertsStatsRequest).actionGet(); AlertsStatsResponse response = alertsClient.alertsStats(alertsStatsRequest).actionGet();
assertTrue(response.isAlertActionManagerStarted()); assertThat(response.isAlertActionManagerStarted(), is(true));
assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED));
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("* * * * * ? *", searchRequest, "hits.total == 1"); BytesReference alertSource = createAlertSource("* * * * * ? *", searchRequest, "hits.total == 1");
alertClient().preparePutAlert("testAlert") alertClient().preparePutAlert("testAlert")
.setAlertSource(alertSource) .source(alertSource)
.get(); .get();
response = alertClient().alertsStats(alertsStatsRequest).actionGet(); response = alertClient().alertsStats(alertsStatsRequest).actionGet();
@ -63,9 +68,9 @@ public class AlertStatsTests extends AbstractAlertingTests {
TimeValue waitTime = new TimeValue(30, TimeUnit.SECONDS); TimeValue waitTime = new TimeValue(30, TimeUnit.SECONDS);
Thread.sleep(waitTime.getMillis()); Thread.sleep(waitTime.getMillis());
assertTrue(response.isAlertActionManagerStarted()); assertThat(response.isAlertActionManagerStarted(), is(true));
assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); assertThat(response.getAlertManagerStarted(), is(AlertsService.State.STARTED));
assertThat(response.getNumberOfRegisteredAlerts(), equalTo(1L)); assertThat(response.getNumberOfRegisteredAlerts(), is(1L));
//assertThat(response.getAlertActionManagerLargestQueueSize(), greaterThan(0L)); //assertThat(response.getAlertActionManagerLargestQueueSize(), greaterThan(0L));
} }
} }

View File

@ -3,41 +3,37 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts; package org.elasticsearch.alerts.test.integration;
import org.elasticsearch.action.count.CountResponse; import org.elasticsearch.action.count.CountResponse;
import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.alerts.actions.Action; import org.elasticsearch.alerts.Alert;
import org.elasticsearch.alerts.actions.Actions; import org.elasticsearch.alerts.actions.ActionBuilders;
import org.elasticsearch.alerts.actions.index.IndexAction;
import org.elasticsearch.alerts.client.AlertsClient; import org.elasticsearch.alerts.client.AlertsClient;
import org.elasticsearch.alerts.condition.script.ScriptCondition;
import org.elasticsearch.alerts.history.FiredAlert; import org.elasticsearch.alerts.history.FiredAlert;
import org.elasticsearch.alerts.history.HistoryStore; import org.elasticsearch.alerts.history.HistoryStore;
import org.elasticsearch.alerts.input.search.SearchInput; import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.scheduler.schedule.CronSchedule;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.transform.SearchTransform;
import org.elasticsearch.alerts.transport.actions.ack.AckAlertResponse; import org.elasticsearch.alerts.transport.actions.ack.AckAlertResponse;
import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse; import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse;
import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse;
import org.elasticsearch.common.unit.TimeValue; 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.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHit;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder;
import static org.elasticsearch.alerts.condition.ConditionBuilders.scriptCondition;
import static org.elasticsearch.alerts.input.InputBuilders.searchInput;
import static org.elasticsearch.alerts.transform.TransformBuilders.searchTransform;
import static org.elasticsearch.alerts.scheduler.schedule.Schedules.cron;
import static org.elasticsearch.alerts.test.AlertsTestUtils.matchAllRequest;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
@ -45,7 +41,7 @@ import static org.hamcrest.core.IsEqual.equalTo;
/** /**
*/ */
public class AlertThrottleTests extends AbstractAlertingTests { public class AlertThrottleTests extends AbstractAlertsIntegrationTests {
@Test @Test
public void testAckThrottle() throws Exception{ public void testAckThrottle() throws Exception{
@ -57,32 +53,22 @@ public class AlertThrottleTests extends AbstractAlertingTests {
assertTrue(dummyEventIndexResponse.isCreated()); assertTrue(dummyEventIndexResponse.isCreated());
refresh(); refresh();
PutAlertResponse putAlertResponse = alertsClient.preparePutAlert()
.alertName("throttled-alert")
.source(alertSourceBuilder()
.schedule(cron("0/5 * * * * ? *"))
.input(searchInput(matchAllRequest().indices("test-index")))
.condition(scriptCondition("hits.total > 0"))
.transform(searchTransform(matchAllRequest().indices("test-index")))
.addAction(ActionBuilders.indexAction("action-index", "action-type"))
.throttlePeriod(TimeValue.timeValueMillis(0)))
.get();
SearchRequest request = createConditionSearchRequest("test-index").source(searchSource().query(matchAllQuery()));
List<Action> actions = new ArrayList<>();
actions.add(new IndexAction(logger, ClientProxy.of(client()), "action-index", "action-type"));
Alert alert = new Alert(
"test-ack-throttle",
new CronSchedule("0/5 * * * * ? *"),
new SearchInput(logger, scriptService(), ClientProxy.of(client()),
request),
new ScriptCondition(logger, scriptService(), new Script("hits.total > 0")),
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request), new Actions(actions), null, new Alert.Status(), new TimeValue(0)
);
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
PutAlertResponse putAlertResponse = alertsClient.preparePutAlert().setAlertName("throttled-alert").setAlertSource(jsonBuilder.bytes()).get();
assertTrue(putAlertResponse.indexResponse().isCreated()); assertTrue(putAlertResponse.indexResponse().isCreated());
Thread.sleep(20000); Thread.sleep(20000);
AckAlertResponse ackResponse = alertsClient.prepareAckAlert("throttled-alert").get(); AckAlertResponse ackResponse = alertsClient.prepareAckAlert("throttled-alert").get();
assertEquals(Alert.Status.AckStatus.State.ACKED, ackResponse.getStatus().ackStatus().state()); Assert.assertEquals(Alert.Status.AckStatus.State.ACKED, ackResponse.getStatus().ackStatus().state());
refresh(); refresh();
SearchResponse searchResponse = client() SearchResponse searchResponse = client()
@ -138,24 +124,16 @@ public class AlertThrottleTests extends AbstractAlertingTests {
assertTrue(dummyEventIndexResponse.isCreated()); assertTrue(dummyEventIndexResponse.isCreated());
refresh(); refresh();
SearchRequest request = createConditionSearchRequest("test-index").source(searchSource().query(matchAllQuery())); PutAlertResponse putAlertResponse = alertsClient.preparePutAlert()
.alertName("throttled-alert")
List<Action> actions = new ArrayList<>(); .source(alertSourceBuilder()
.schedule(cron("0/5 * * * * ? *"))
actions.add(new IndexAction(logger, ClientProxy.of(client()), "action-index", "action-type")); .input(searchInput(matchAllRequest().indices("test-index")))
.condition(scriptCondition("hits.total > 0"))
Alert alert = new Alert( .transform(searchTransform(matchAllRequest().indices("test-index")))
"test-time-throttle", .addAction(ActionBuilders.indexAction("action-index", "action-type"))
new CronSchedule("0/5 * * * * ? *"), .throttlePeriod(TimeValue.timeValueSeconds(10)))
new SearchInput(logger, scriptService(), ClientProxy.of(client()), request), .get();
new ScriptCondition(logger, scriptService(), new Script("hits.total > 0")),
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), request),
new Actions(actions), null, new Alert.Status(), new TimeValue(10, TimeUnit.SECONDS));
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
PutAlertResponse putAlertResponse = alertsClient.preparePutAlert().setAlertName("throttled-alert").setAlertSource(jsonBuilder.bytes()).get();
assertTrue(putAlertResponse.indexResponse().isCreated()); assertTrue(putAlertResponse.indexResponse().isCreated());
forceFullSleepTime(new TimeValue(5, TimeUnit.SECONDS)); forceFullSleepTime(new TimeValue(5, TimeUnit.SECONDS));

View File

@ -3,18 +3,23 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts; package org.elasticsearch.alerts.test.integration;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.alerts.AlertsException;
import org.elasticsearch.alerts.AlertsStore;
import org.elasticsearch.alerts.client.AlertSourceBuilder;
import org.elasticsearch.alerts.client.AlertsClient; import org.elasticsearch.alerts.client.AlertsClient;
import org.elasticsearch.alerts.scheduler.schedule.IntervalSchedule;
import org.elasticsearch.alerts.support.AlertUtils; import org.elasticsearch.alerts.support.AlertUtils;
import org.elasticsearch.alerts.support.Variables; import org.elasticsearch.alerts.support.Variables;
import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertRequest; import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertRequest;
import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse; import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse;
import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse; import org.elasticsearch.alerts.transport.actions.get.GetAlertResponse;
import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.FilterBuilders;
@ -28,6 +33,12 @@ import org.junit.Test;
import java.util.Locale; import java.util.Locale;
import static org.elasticsearch.alerts.actions.ActionBuilders.indexAction;
import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder;
import static org.elasticsearch.alerts.condition.ConditionBuilders.scriptCondition;
import static org.elasticsearch.alerts.input.InputBuilders.searchInput;
import static org.elasticsearch.alerts.scheduler.schedule.Schedules.cron;
import static org.elasticsearch.alerts.scheduler.schedule.Schedules.interval;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.FilterBuilders.rangeFilter; import static org.elasticsearch.index.query.FilterBuilders.rangeFilter;
import static org.elasticsearch.index.query.QueryBuilders.*; import static org.elasticsearch.index.query.QueryBuilders.*;
@ -39,7 +50,7 @@ import static org.hamcrest.Matchers.is;
/** /**
*/ */
public class BasicAlertingTest extends AbstractAlertingTests { public class BasicAlertsTests extends AbstractAlertsIntegrationTests {
@Test @Test
public void testIndexAlert() throws Exception { public void testIndexAlert() throws Exception {
@ -47,10 +58,12 @@ public class BasicAlertingTest extends AbstractAlertingTests {
createIndex("my-index"); createIndex("my-index");
// Have a sample document in the index, the alert is going to evaluate // Have a sample document in the index, the alert is going to evaluate
client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); client().prepareIndex("my-index", "my-type").setSource("field", "value").get();
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
alertsClient.preparePutAlert("my-first-alert") alertsClient.preparePutAlert("my-first-alert")
.setAlertSource(alertSource) .source(alertSourceBuilder()
.schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))
.input(searchInput(searchRequest))
.condition(scriptCondition("hits.total == 1")))
.get(); .get();
assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1); assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1);
@ -62,10 +75,12 @@ public class BasicAlertingTest extends AbstractAlertingTests {
@Test @Test
public void testIndexAlert_registerAlertBeforeTargetIndex() throws Exception { public void testIndexAlert_registerAlertBeforeTargetIndex() throws Exception {
AlertsClient alertsClient = alertClient(); AlertsClient alertsClient = alertClient();
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
alertsClient.preparePutAlert("my-first-alert") alertsClient.preparePutAlert("my-first-alert")
.setAlertSource(alertSource) .source(alertSourceBuilder()
.schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))
.input(searchInput(searchRequest))
.condition(scriptCondition("hits.total == 1")))
.get(); .get();
// The alert's condition won't meet because there is no data that matches with the query // The alert's condition won't meet because there is no data that matches with the query
@ -82,10 +97,12 @@ public class BasicAlertingTest extends AbstractAlertingTests {
createIndex("my-index"); createIndex("my-index");
// Have a sample document in the index, the alert is going to evaluate // Have a sample document in the index, the alert is going to evaluate
client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); client().prepareIndex("my-index", "my-type").setSource("field", "value").get();
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery())); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(matchAllQuery()));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
PutAlertResponse indexResponse = alertsClient.preparePutAlert("my-first-alert") PutAlertResponse indexResponse = alertsClient.preparePutAlert("my-first-alert")
.setAlertSource(alertSource) .source(alertSourceBuilder()
.schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))
.input(searchInput(searchRequest))
.condition(scriptCondition("hits.total == 1")))
.get(); .get();
assertThat(indexResponse.indexResponse().isCreated(), is(true)); assertThat(indexResponse.indexResponse().isCreated(), is(true));
@ -117,13 +134,13 @@ public class BasicAlertingTest extends AbstractAlertingTests {
alertSource.startObject("schedule").field("cron", "0/5 * * * * ? *").endObject(); alertSource.startObject("schedule").field("cron", "0/5 * * * * ? *").endObject();
alertSource.startObject("condition").startObject("script").field("script", "return true").field("request"); alertSource.startObject("condition").startObject("script").field("script", "return true").field("request");
AlertUtils.writeSearchRequest(createConditionSearchRequest(), alertSource, ToXContent.EMPTY_PARAMS); AlertUtils.writeSearchRequest(AlertsTestUtils.newInputSearchRequest(), alertSource, ToXContent.EMPTY_PARAMS);
alertSource.endObject(); alertSource.endObject();
alertSource.endObject(); alertSource.endObject();
try { try {
alertsClient.preparePutAlert("my-first-alert") alertsClient.preparePutAlert("my-first-alert")
.setAlertSource(alertSource.bytes()) .source(alertSource.bytes())
.get(); .get();
fail(); fail();
} catch (AlertsException e) { } catch (AlertsException e) {
@ -145,12 +162,14 @@ public class BasicAlertingTest extends AbstractAlertingTests {
createIndex("my-index"); createIndex("my-index");
// Have a sample document in the index, the alert is going to evaluate // Have a sample document in the index, the alert is going to evaluate
client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); client().prepareIndex("my-index", "my-type").setSource("field", "value").get();
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery())); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(matchAllQuery()));
searchRequest.searchType(SearchType.QUERY_THEN_FETCH); searchRequest.searchType(SearchType.QUERY_THEN_FETCH);
// By accessing the actual hit we know that the fetch phase has been performed // By accessing the actual hit we know that the fetch phase has been performed
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits?.hits[0]._score == 1.0");
PutAlertResponse indexResponse = alertsClient.preparePutAlert("my-first-alert") PutAlertResponse indexResponse = alertsClient.preparePutAlert("my-first-alert")
.setAlertSource(alertSource) .source(alertSourceBuilder()
.schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS))
.input(searchInput(searchRequest))
.condition(scriptCondition("hits?.hits[0]._score == 1.0")))
.get(); .get();
assertThat(indexResponse.indexResponse().isCreated(), is(true)); assertThat(indexResponse.indexResponse().isCreated(), is(true));
assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1); assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1);
@ -158,19 +177,27 @@ public class BasicAlertingTest extends AbstractAlertingTests {
@Test @Test
public void testModifyAlerts() throws Exception { public void testModifyAlerts() throws Exception {
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(matchAllQuery())); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index")
.source(searchSource().query(matchAllQuery()));
AlertSourceBuilder source = alertSourceBuilder()
.schedule(cron("0/5 * * * * ? *"))
.input(searchInput(searchRequest))
.addAction(indexAction("my-index", "trail"));
alertClient().preparePutAlert("1") alertClient().preparePutAlert("1")
.setAlertSource(createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1")) .source(source.condition(scriptCondition("hits.total == 1")))
.get(); .get();
assertAlertWithMinimumPerformedActionsCount("1", 0, false); assertAlertWithMinimumPerformedActionsCount("1", 0, false);
alertClient().preparePutAlert("1") alertClient().preparePutAlert("1")
.setAlertSource(createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 0")) .source(source.condition(scriptCondition("hits.total == 0")))
.get(); .get();
assertAlertWithMinimumPerformedActionsCount("1", 1, false); assertAlertWithMinimumPerformedActionsCount("1", 1, false);
alertClient().preparePutAlert("1") alertClient().preparePutAlert("1")
.setAlertSource(createAlertSource("0/5 * * * * ? 2020", searchRequest, "hits.total == 0")) .source(source.schedule(cron("0/5 * * * * ? 2020")).condition(scriptCondition("hits.total == 0")))
.get(); .get();
Thread.sleep(5000); Thread.sleep(5000);
@ -181,12 +208,13 @@ public class BasicAlertingTest extends AbstractAlertingTests {
@Test @Test
public void testAggregations() throws Exception { public void testAggregations() throws Exception {
class R implements Runnable {
class Indexer extends Thread {
private final long sleepTime; private final long sleepTime;
private final long totalTime; private final long totalTime;
R(long sleepTime, long totalTime) { Indexer(long sleepTime, long totalTime) {
this.sleepTime = sleepTime; this.sleepTime = sleepTime;
this.totalTime = totalTime; this.totalTime = totalTime;
} }
@ -207,24 +235,30 @@ public class BasicAlertingTest extends AbstractAlertingTests {
} }
assertAcked(prepareCreate("my-index").addMapping("my-type", "_timestamp", "enabled=true")); assertAcked(prepareCreate("my-index").addMapping("my-type", "_timestamp", "enabled=true"));
SearchRequest searchRequest = createConditionSearchRequest("my-index").source( SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(
searchSource() searchSource()
.query(QueryBuilders.constantScoreQuery(FilterBuilders.rangeFilter("_timestamp").from("{{scheduled_fire_time}}||-1m").to("{{scheduled_fire_time}}"))) .query(QueryBuilders.constantScoreQuery(FilterBuilders.rangeFilter("_timestamp").from("{{scheduled_fire_time}}||-1m").to("{{scheduled_fire_time}}")))
.aggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp").interval(DateHistogram.Interval.SECOND).order(Histogram.Order.COUNT_DESC)) .aggregation(AggregationBuilders.dateHistogram("rate").field("_timestamp").interval(DateHistogram.Interval.SECOND).order(Histogram.Order.COUNT_DESC)));
); // BytesReference reference = createAlertSource("* 0/1 * * * ? *", searchRequest, "aggregations.rate.buckets[0]?.doc_count > 5");
BytesReference reference = createAlertSource("* 0/1 * * * ? *", searchRequest, "aggregations.rate.buckets[0]?.doc_count > 5"); alertClient().preparePutAlert("rate-alert")
alertClient().preparePutAlert("rate-alert").setAlertSource(reference).get(); .source(alertSourceBuilder()
.schedule(cron("* 0/1 * * * ? *"))
.input(searchInput(searchRequest))
.condition(scriptCondition("aggregations.rate.buckets[0]?.doc_count > 5"))
.addAction(indexAction("my-index", "trail")))
.get();
Thread indexThread = new Thread(new R(500, 60000)); Indexer indexer = new Indexer(500, 60000);
indexThread.start(); indexer.start();
indexThread.join(); indexer.join();
assertAlertWithExactPerformedActionsCount("rate-alert", 0); assertAlertWithExactPerformedActionsCount("rate-alert", 0);
assertAlertWithNoActionNeeded("rate-alert", 1); assertAlertWithNoActionNeeded("rate-alert", 1);
indexThread = new Thread(new R(100, 60000)); indexer = new Indexer(100, 60000);
indexThread.start(); indexer.start();
indexThread.join(); indexer.join();
assertAlertWithMinimumPerformedActionsCount("rate-alert", 1); assertAlertWithMinimumPerformedActionsCount("rate-alert", 1);
} }
@ -235,7 +269,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
@Test @Test
public void testConditionSearchWithSource() throws Exception { public void testConditionSearchWithSource() throws Exception {
testConditionSearch( testConditionSearch(
createConditionSearchRequest("my-index").source(searchSourceBuilder) AlertsTestUtils.newInputSearchRequest("my-index").source(searchSourceBuilder)
); );
} }
@ -246,7 +280,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
.setId("my-template") .setId("my-template")
.setSource(jsonBuilder().startObject().field("template").value(searchSourceBuilder).endObject()) .setSource(jsonBuilder().startObject().field("template").value(searchSourceBuilder).endObject())
.get(); .get();
SearchRequest searchRequest = createConditionSearchRequest("my-index"); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index");
searchRequest.templateName("my-template"); searchRequest.templateName("my-template");
searchRequest.templateType(ScriptService.ScriptType.INDEXED); searchRequest.templateType(ScriptService.ScriptType.INDEXED);
testConditionSearch(searchRequest); testConditionSearch(searchRequest);
@ -259,7 +293,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
alertClient().prepareDeleteAlert(alertName).get(); alertClient().prepareDeleteAlert(alertName).get();
alertClient().preparePutAlert(alertName) alertClient().preparePutAlert(alertName)
.setAlertSource(createAlertSource(String.format(Locale.ROOT, "0/%s * * * * ? *", (scheduleTimeInMs / 1000)), request, "return hits.total >= 3")) .source(createAlertSource(String.format(Locale.ROOT, "0/%s * * * * ? *", (scheduleTimeInMs / 1000)), request, "return hits.total >= 3"))
.get(); .get();
long time1 = System.currentTimeMillis(); long time1 = System.currentTimeMillis();

View File

@ -3,12 +3,15 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts; package org.elasticsearch.alerts.test.integration;
import org.elasticsearch.action.WriteConsistencyLevel; import org.elasticsearch.action.WriteConsistencyLevel;
import org.elasticsearch.action.count.CountResponse; import org.elasticsearch.action.count.CountResponse;
import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.alerts.Alert;
import org.elasticsearch.alerts.AlertsService;
import org.elasticsearch.alerts.AlertsStore;
import org.elasticsearch.alerts.actions.Action; import org.elasticsearch.alerts.actions.Action;
import org.elasticsearch.alerts.actions.Actions; import org.elasticsearch.alerts.actions.Actions;
import org.elasticsearch.alerts.condition.script.ScriptCondition; import org.elasticsearch.alerts.condition.script.ScriptCondition;
@ -18,6 +21,8 @@ import org.elasticsearch.alerts.input.search.SearchInput;
import org.elasticsearch.alerts.scheduler.schedule.CronSchedule; import org.elasticsearch.alerts.scheduler.schedule.CronSchedule;
import org.elasticsearch.alerts.support.Script; import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy; import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.elasticsearch.alerts.transform.SearchTransform; import org.elasticsearch.alerts.transform.SearchTransform;
import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse;
import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse;
@ -43,13 +48,13 @@ import static org.hamcrest.core.IsEqual.equalTo;
/** /**
*/ */
public class BootStrapTest extends AbstractAlertingTests { public class BootStrapTests extends AbstractAlertsIntegrationTests {
@Test @Test
public void testBootStrapAlerts() throws Exception { public void testBootStrapAlerts() throws Exception {
ensureAlertingStarted(); ensureAlertingStarted();
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("0 0/5 * * * ? *", searchRequest, "hits.total == 1"); BytesReference alertSource = createAlertSource("0 0/5 * * * ? *", searchRequest, "hits.total == 1");
client().prepareIndex(AlertsStore.ALERT_INDEX, AlertsStore.ALERT_TYPE, "my-first-alert") client().prepareIndex(AlertsStore.ALERT_INDEX, AlertsStore.ALERT_TYPE, "my-first-alert")
.setSource(alertSource) .setSource(alertSource)
@ -76,16 +81,17 @@ public class BootStrapTest extends AbstractAlertingTests {
assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED)); assertThat(response.getAlertManagerStarted(), equalTo(AlertsService.State.STARTED));
assertThat(response.getNumberOfRegisteredAlerts(), equalTo(0L)); assertThat(response.getNumberOfRegisteredAlerts(), equalTo(0L));
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
Alert alert = new Alert( Alert alert = new Alert(
"test-serialization", "test-serialization",
new CronSchedule("0/5 * * * * ? 2035"), //Set this into the future so we don't get any extra runs new CronSchedule("0/5 * * * * ? 2035"), //Set this into the future so we don't get any extra runs
new SearchInput(logger, scriptService(), ClientProxy.of(client()), searchRequest), new SearchInput(logger, scriptService(), ClientProxy.of(client()), searchRequest),
new ScriptCondition(logger, scriptService(), new Script("return true")), new ScriptCondition(logger, scriptService(), new Script("return true")),
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest), new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest),
new Actions(new ArrayList<Action>()), null, new Alert.Status(), new TimeValue(0) new Actions(new ArrayList<Action>()),
null, // metadata
); new TimeValue(0),
new Alert.Status());
XContentBuilder builder = jsonBuilder().value(alert); XContentBuilder builder = jsonBuilder().value(alert);
IndexResponse indexResponse = client().prepareIndex(AlertsStore.ALERT_INDEX, AlertsStore.ALERT_TYPE, alert.name()) IndexResponse indexResponse = client().prepareIndex(AlertsStore.ALERT_INDEX, AlertsStore.ALERT_TYPE, alert.name())
@ -124,7 +130,7 @@ public class BootStrapTest extends AbstractAlertingTests {
DateTime now = new DateTime(DateTimeZone.UTC); DateTime now = new DateTime(DateTimeZone.UTC);
long numberOfAlertHistoryIndices = randomIntBetween(2,8); long numberOfAlertHistoryIndices = randomIntBetween(2,8);
long numberOfAlertHistoryEntriesPerIndex = randomIntBetween(5,10); long numberOfAlertHistoryEntriesPerIndex = randomIntBetween(5,10);
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
for (int i = 0; i < numberOfAlertHistoryIndices; i++) { for (int i = 0; i < numberOfAlertHistoryIndices; i++) {
DateTime historyIndexDate = now.minus((new TimeValue(i, TimeUnit.DAYS)).getMillis()); DateTime historyIndexDate = now.minus((new TimeValue(i, TimeUnit.DAYS)).getMillis());
@ -143,14 +149,13 @@ public class BootStrapTest extends AbstractAlertingTests {
new ScriptCondition(logger, scriptService(), new Script("return true")), new ScriptCondition(logger, scriptService(), new Script("return true")),
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest), new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest),
new Actions(new ArrayList<Action>()), new Actions(new ArrayList<Action>()),
null, null, // metatdata
new Alert.Status(), new TimeValue(0),
new TimeValue(0) new Alert.Status());
);
XContentBuilder jsonBuilder = jsonBuilder(); XContentBuilder jsonBuilder = jsonBuilder();
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS); alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
PutAlertResponse putAlertResponse = alertClient().preparePutAlert(alert.name()).setAlertSource(jsonBuilder.bytes()).get(); PutAlertResponse putAlertResponse = alertClient().preparePutAlert(alert.name()).source(jsonBuilder.bytes()).get();
assertTrue(putAlertResponse.indexResponse().isCreated()); assertTrue(putAlertResponse.indexResponse().isCreated());
FiredAlert firedAlert = new FiredAlert(alert, historyIndexDate, historyIndexDate); FiredAlert firedAlert = new FiredAlert(alert, historyIndexDate, historyIndexDate);

View File

@ -3,10 +3,13 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts; package org.elasticsearch.alerts.test.integration;
import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.alerts.AlertsService;
import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse; import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse;
import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse; import org.elasticsearch.alerts.transport.actions.stats.AlertsStatsResponse;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
@ -20,6 +23,7 @@ import org.elasticsearch.discovery.DiscoverySettings;
import org.elasticsearch.discovery.MasterNotDiscoveredException; import org.elasticsearch.discovery.MasterNotDiscoveredException;
import org.elasticsearch.discovery.zen.elect.ElectMasterService; import org.elasticsearch.discovery.zen.elect.ElectMasterService;
import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.elasticsearch.test.discovery.ClusterDiscoveryConfiguration; import org.elasticsearch.test.discovery.ClusterDiscoveryConfiguration;
import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.test.junit.annotations.TestLogging;
import org.junit.Test; import org.junit.Test;
@ -28,14 +32,15 @@ import java.util.concurrent.TimeUnit;
import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.core.Is.is; import static org.hamcrest.core.Is.is;
/** /**
*/ */
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false, numDataNodes = 0) @ClusterScope(scope = TEST, numClientNodes = 0, transportClientRatio = 0, randomDynamicTemplates = false, numDataNodes = 0)
public class NoMasterNodeTests extends AbstractAlertingTests { public class NoMasterNodeTests extends AbstractAlertsIntegrationTests {
private ClusterDiscoveryConfiguration.UnicastZen config; private ClusterDiscoveryConfiguration.UnicastZen config;
@ -60,10 +65,10 @@ public class NoMasterNodeTests extends AbstractAlertingTests {
// Have a sample document in the index, the alert is going to evaluate // Have a sample document in the index, the alert is going to evaluate
client().prepareIndex("my-index", "my-type").setSource("field", "value").get(); client().prepareIndex("my-index", "my-type").setSource("field", "value").get();
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1"); BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
alertClient().preparePutAlert("my-first-alert") alertClient().preparePutAlert("my-first-alert")
.setAlertSource(alertSource) .source(alertSource)
.get(); .get();
assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1); assertAlertWithMinimumPerformedActionsCount("my-first-alert", 1);
@ -88,7 +93,7 @@ public class NoMasterNodeTests extends AbstractAlertingTests {
// Add a new alert and wait for its condition to be met // Add a new alert and wait for its condition to be met
alertClient().preparePutAlert("my-second-alert") alertClient().preparePutAlert("my-second-alert")
.setAlertSource(alertSource) .source(alertSource)
.get(); .get();
assertAlertWithMinimumPerformedActionsCount("my-second-alert", 1); assertAlertWithMinimumPerformedActionsCount("my-second-alert", 1);
} }
@ -108,10 +113,10 @@ public class NoMasterNodeTests extends AbstractAlertingTests {
ensureAlertingStarted(); ensureAlertingStarted();
for (int i = 1; i <= numberOfAlerts; i++) { for (int i = 1; i <= numberOfAlerts; i++) {
String alertName = "alert" + i; String alertName = "alert" + i;
SearchRequest searchRequest = createConditionSearchRequest("my-index").source(searchSource().query(termQuery("field", "value"))); SearchRequest searchRequest = AlertsTestUtils.newInputSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1"); BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
alertClient().preparePutAlert(alertName) alertClient().preparePutAlert(alertName)
.setAlertSource(alertSource) .source(alertSource)
.get(); .get();
} }
ensureGreen(); ensureGreen();

View File

@ -3,40 +3,34 @@
* or more contributor license agreements. Licensed under the Elastic License; * or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License. * you may not use this file except in compliance with the Elastic License.
*/ */
package org.elasticsearch.alerts; package org.elasticsearch.alerts.test.integration;
import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.alerts.actions.Action; import org.elasticsearch.alerts.scheduler.schedule.IntervalSchedule.Interval;
import org.elasticsearch.alerts.actions.Actions; import org.elasticsearch.alerts.test.AbstractAlertsIntegrationTests;
import org.elasticsearch.alerts.actions.index.IndexAction; import org.elasticsearch.alerts.test.AlertsTestUtils;
import org.elasticsearch.alerts.condition.script.ScriptCondition;
import org.elasticsearch.alerts.input.search.SearchInput;
import org.elasticsearch.alerts.scheduler.schedule.CronSchedule;
import org.elasticsearch.alerts.support.Script;
import org.elasticsearch.alerts.support.init.proxy.ClientProxy;
import org.elasticsearch.alerts.transform.SearchTransform; import org.elasticsearch.alerts.transform.SearchTransform;
import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse; import org.elasticsearch.alerts.transport.actions.put.PutAlertResponse;
import org.elasticsearch.common.unit.TimeValue; 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.script.ScriptService;
import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHit;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import static org.elasticsearch.alerts.actions.ActionBuilders.indexAction;
import static org.elasticsearch.alerts.client.AlertSourceBuilder.alertSourceBuilder;
import static org.elasticsearch.alerts.input.InputBuilders.searchInput;
import static org.elasticsearch.alerts.transform.TransformBuilders.searchTransform;
import static org.elasticsearch.alerts.scheduler.schedule.Schedules.interval;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource; import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
/** /**
*/ */
public class TransformSearchTest extends AbstractAlertingTests { public class TransformSearchTests extends AbstractAlertsIntegrationTests {
@Test @Test
public void testTransformSearchRequest() throws Exception { public void testTransformSearchRequest() throws Exception {
@ -46,31 +40,23 @@ public class TransformSearchTest extends AbstractAlertingTests {
index("my-payload-index","payload", "mytestresult"); index("my-payload-index","payload", "mytestresult");
refresh(); refresh();
SearchRequest conditionRequest = createConditionSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery())); SearchRequest inputRequest = AlertsTestUtils.newInputSearchRequest("my-condition-index").source(searchSource().query(matchAllQuery()));
SearchRequest transformRequest = createConditionSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery())); SearchRequest transformRequest = AlertsTestUtils.newInputSearchRequest("my-payload-index").source(searchSource().query(matchAllQuery()));
transformRequest.searchType(SearchTransform.DEFAULT_SEARCH_TYPE); transformRequest.searchType(SearchTransform.DEFAULT_SEARCH_TYPE);
List<Action> actions = new ArrayList<>();
actions.add(new IndexAction(logger, ClientProxy.of(client()), "my-payload-output","result"));
Map<String, Object> metadata = new HashMap<>(); Map<String, Object> metadata = new HashMap<>();
metadata.put("foo", "bar"); metadata.put("foo", "bar");
metadata.put("list", "baz"); metadata.put("list", "baz");
Alert alert = new Alert( PutAlertResponse putAlertResponse = alertClient().preparePutAlert("test-payload")
"test-serialization", .source(alertSourceBuilder()
new CronSchedule("0/5 * * * * ? *"), .schedule(interval(5, Interval.Unit.SECONDS))
new SearchInput(logger, scriptService(), ClientProxy.of(client()), .input(searchInput(inputRequest))
conditionRequest), .transform(searchTransform(transformRequest))
new ScriptCondition(logger, scriptService(), new Script("return true")), .addAction(indexAction("my-payload-output", "result"))
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), transformRequest), .metadata(metadata)
new Actions(actions), metadata, new Alert.Status(), new TimeValue(0) .throttlePeriod(TimeValue.timeValueSeconds(0)))
); .get();
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
alert.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
PutAlertResponse putAlertResponse = alertClient().preparePutAlert("test-payload").setAlertSource(jsonBuilder.bytes()).get();
assertTrue(putAlertResponse.indexResponse().isCreated()); assertTrue(putAlertResponse.indexResponse().isCreated());
assertAlertWithMinimumPerformedActionsCount("test-payload", 1, false); assertAlertWithMinimumPerformedActionsCount("test-payload", 1, false);

View File

@ -24,9 +24,7 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -41,7 +39,7 @@ public class ScriptTransformTests extends ElasticsearchTestCase {
ScriptService.ScriptType type = randomFrom(ScriptService.ScriptType.values()); ScriptService.ScriptType type = randomFrom(ScriptService.ScriptType.values());
Map<String, Object> params = Collections.emptyMap(); Map<String, Object> params = Collections.emptyMap();
Script script = new Script("_script", type, "_lang", params); Script script = new Script("_script", type, "_lang", params);
ScriptTransform transform = new ScriptTransform(script, service); ScriptTransform transform = new ScriptTransform(service, script);
DateTime now = new DateTime(); DateTime now = new DateTime();
ExecutionContext ctx = mock(ExecutionContext.class); ExecutionContext ctx = mock(ExecutionContext.class);