Watcher: Adding PagerDuty Action
* This action enables sending notifications to pager duty services. * Utilizes pager duty's REST API * Similar to the `email`, `hipchat` and `slack` actions, multiple `pagerduty` accounts can be configured, each with its own Service API key * A `pagerduty` account is roughly mapped to a service in your pagerduty service. * `pagerduty` actions are associated with an account, or if not, their events will be sent via the default account. * An incident can be acknowledged, resolved or triggered Closes elastic/elasticsearch#492 Original commit: elastic/x-pack-elasticsearch@72cc21d119
This commit is contained in:
parent
e8eb0fa312
commit
9709036024
|
@ -27,6 +27,8 @@ import org.elasticsearch.watcher.actions.email.service.EmailService;
|
|||
import org.elasticsearch.watcher.actions.email.service.InternalEmailService;
|
||||
import org.elasticsearch.watcher.actions.hipchat.service.HipChatService;
|
||||
import org.elasticsearch.watcher.actions.hipchat.service.InternalHipChatService;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.InternalPagerDutyService;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyService;
|
||||
import org.elasticsearch.watcher.actions.slack.service.InternalSlackService;
|
||||
import org.elasticsearch.watcher.actions.slack.service.SlackService;
|
||||
import org.elasticsearch.watcher.client.WatcherClientModule;
|
||||
|
@ -163,6 +165,7 @@ public class WatcherPlugin extends Plugin {
|
|||
EmailService.class,
|
||||
HipChatService.class,
|
||||
SlackService.class,
|
||||
PagerDutyService.class,
|
||||
HttpClient.class,
|
||||
WatcherSettingsValidation.class);
|
||||
}
|
||||
|
@ -193,6 +196,7 @@ public class WatcherPlugin extends Plugin {
|
|||
module.registerSetting(InternalSlackService.SLACK_ACCOUNT_SETTING);
|
||||
module.registerSetting(InternalEmailService.EMAIL_ACCOUNT_SETTING);
|
||||
module.registerSetting(InternalHipChatService.HIPCHAT_ACCOUNT_SETTING);
|
||||
module.registerSetting(InternalPagerDutyService.PAGERDUTY_ACCOUNT_SETTING);
|
||||
}
|
||||
|
||||
public void onModule(NetworkModule module) {
|
||||
|
|
|
@ -10,6 +10,8 @@ import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
|||
import org.elasticsearch.watcher.actions.hipchat.HipChatAction;
|
||||
import org.elasticsearch.watcher.actions.index.IndexAction;
|
||||
import org.elasticsearch.watcher.actions.logging.LoggingAction;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.PagerDutyAction;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.IncidentEvent;
|
||||
import org.elasticsearch.watcher.actions.slack.SlackAction;
|
||||
import org.elasticsearch.watcher.actions.slack.service.message.SlackMessage;
|
||||
import org.elasticsearch.watcher.actions.webhook.WebhookAction;
|
||||
|
@ -87,4 +89,16 @@ public final class ActionBuilders {
|
|||
public static SlackAction.Builder slackAction(String account, SlackMessage.Template message) {
|
||||
return SlackAction.builder(account, message);
|
||||
}
|
||||
|
||||
public static PagerDutyAction.Builder triggerPagerDutyAction(String account, String description) {
|
||||
return pagerDutyAction(IncidentEvent.templateBuilder(description).setAccount(account));
|
||||
}
|
||||
|
||||
public static PagerDutyAction.Builder pagerDutyAction(IncidentEvent.Template.Builder event) {
|
||||
return PagerDutyAction.builder(event.build());
|
||||
}
|
||||
|
||||
public static PagerDutyAction.Builder pagerDutyAction(IncidentEvent.Template event) {
|
||||
return PagerDutyAction.builder(event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@ import org.elasticsearch.watcher.actions.index.IndexAction;
|
|||
import org.elasticsearch.watcher.actions.index.IndexActionFactory;
|
||||
import org.elasticsearch.watcher.actions.logging.LoggingAction;
|
||||
import org.elasticsearch.watcher.actions.logging.LoggingActionFactory;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.PagerDutyAction;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.PagerDutyActionFactory;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.InternalPagerDutyService;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyService;
|
||||
import org.elasticsearch.watcher.actions.slack.SlackAction;
|
||||
import org.elasticsearch.watcher.actions.slack.SlackActionFactory;
|
||||
import org.elasticsearch.watcher.actions.slack.service.InternalSlackService;
|
||||
|
@ -43,6 +47,7 @@ public class WatcherActionModule extends AbstractModule {
|
|||
registerAction(LoggingAction.TYPE, LoggingActionFactory.class);
|
||||
registerAction(HipChatAction.TYPE, HipChatActionFactory.class);
|
||||
registerAction(SlackAction.TYPE, SlackActionFactory.class);
|
||||
registerAction(PagerDutyAction.TYPE, PagerDutyActionFactory.class);
|
||||
}
|
||||
|
||||
public void registerAction(String type, Class<? extends ActionFactory> parserType) {
|
||||
|
@ -60,11 +65,21 @@ public class WatcherActionModule extends AbstractModule {
|
|||
|
||||
bind(ActionRegistry.class).asEagerSingleton();
|
||||
|
||||
// email
|
||||
bind(HtmlSanitizer.class).asEagerSingleton();
|
||||
bind(InternalEmailService.class).asEagerSingleton();
|
||||
bind(EmailService.class).to(InternalEmailService.class).asEagerSingleton();
|
||||
bind(HipChatService.class).to(InternalHipChatService.class).asEagerSingleton();
|
||||
bind(SlackService.class).to(InternalSlackService.class).asEagerSingleton();
|
||||
|
||||
// hipchat
|
||||
bind(InternalHipChatService.class).asEagerSingleton();
|
||||
bind(HipChatService.class).to(InternalHipChatService.class);
|
||||
|
||||
// slack
|
||||
bind(InternalSlackService.class).asEagerSingleton();
|
||||
bind(SlackService.class).to(InternalSlackService.class);
|
||||
|
||||
// pager duty
|
||||
bind(InternalPagerDutyService.class).asEagerSingleton();
|
||||
bind(PagerDutyService.class).to(InternalPagerDutyService.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty;
|
||||
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.ExecutableAction;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyAccount;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyService;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.SentEvent;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.IncidentEvent;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.support.Variables;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ExecutablePagerDutyAction extends ExecutableAction<PagerDutyAction> {
|
||||
|
||||
private final TextTemplateEngine templateEngine;
|
||||
private final PagerDutyService pagerDutyService;
|
||||
|
||||
public ExecutablePagerDutyAction(PagerDutyAction action, ESLogger logger, PagerDutyService pagerDutyService, TextTemplateEngine templateEngine) {
|
||||
super(action, logger);
|
||||
this.pagerDutyService = pagerDutyService;
|
||||
this.templateEngine = templateEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action.Result execute(final String actionId, WatchExecutionContext ctx, Payload payload) throws Exception {
|
||||
|
||||
PagerDutyAccount account = action.event.account != null ?
|
||||
pagerDutyService.getAccount(action.event.account) :
|
||||
pagerDutyService.getDefaultAccount();
|
||||
|
||||
if (account == null) {
|
||||
// the account associated with this action was deleted
|
||||
throw new IllegalStateException("account [" + action.event.account + "] was not found. perhaps it was deleted");
|
||||
}
|
||||
|
||||
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
|
||||
IncidentEvent event = action.event.render(ctx.watch().id(), actionId, templateEngine, model, account.getDefaults());
|
||||
|
||||
if (ctx.simulateAction(actionId)) {
|
||||
return new PagerDutyAction.Result.Simulated(event);
|
||||
}
|
||||
|
||||
SentEvent sentEvent = account.send(event, payload);
|
||||
return new PagerDutyAction.Result.Executed(account.getName(), sentEvent);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty;
|
||||
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.IncidentEvent;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.SentEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PagerDutyAction implements Action {
|
||||
|
||||
public static final String TYPE = "pagerduty";
|
||||
|
||||
final IncidentEvent.Template event;
|
||||
|
||||
public PagerDutyAction(IncidentEvent.Template event) {
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PagerDutyAction that = (PagerDutyAction) o;
|
||||
return Objects.equals(event, that.event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
event.toXContent(builder, params);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static PagerDutyAction parse(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
IncidentEvent.Template eventTemplate = IncidentEvent.Template.parse(watchId, actionId, parser);
|
||||
return new PagerDutyAction(eventTemplate);
|
||||
}
|
||||
|
||||
public static Builder builder(IncidentEvent.Template event) {
|
||||
return new Builder(new PagerDutyAction(event));
|
||||
}
|
||||
|
||||
public interface Result {
|
||||
|
||||
class Executed extends Action.Result implements Result {
|
||||
|
||||
private final String account;
|
||||
private final SentEvent sentEvent;
|
||||
|
||||
public Executed(String account, SentEvent sentEvent) {
|
||||
super(TYPE, status(sentEvent));
|
||||
this.account = account;
|
||||
this.sentEvent = sentEvent;
|
||||
}
|
||||
|
||||
public SentEvent sentEvent() {
|
||||
return sentEvent;
|
||||
}
|
||||
|
||||
public String account() {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(type);
|
||||
builder.field(XField.SENT_EVENT.getPreferredName(), sentEvent, params);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
static Status status(SentEvent sentEvent) {
|
||||
return sentEvent.successful() ? Status.SUCCESS : Status.FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
class Simulated extends Action.Result implements Result {
|
||||
|
||||
private final IncidentEvent event;
|
||||
|
||||
protected Simulated(IncidentEvent event) {
|
||||
super(TYPE, Status.SIMULATED);
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public IncidentEvent event() {
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject(type)
|
||||
.field(XField.EVENT.getPreferredName(), event, params)
|
||||
.endObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder implements Action.Builder<PagerDutyAction> {
|
||||
|
||||
final PagerDutyAction action;
|
||||
|
||||
public Builder(PagerDutyAction action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagerDutyAction build() {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
|
||||
public interface XField {
|
||||
ParseField SENT_EVENT = new ParseField("sent_event");
|
||||
ParseField EVENT = new ParseField("event");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.ActionFactory;
|
||||
import org.elasticsearch.watcher.actions.hipchat.ExecutableHipChatAction;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyAccount;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyService;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PagerDutyActionFactory extends ActionFactory<PagerDutyAction, ExecutablePagerDutyAction> {
|
||||
|
||||
private final TextTemplateEngine templateEngine;
|
||||
private final PagerDutyService pagerDutyService;
|
||||
|
||||
@Inject
|
||||
public PagerDutyActionFactory(Settings settings, TextTemplateEngine templateEngine, PagerDutyService pagerDutyService) {
|
||||
super(Loggers.getLogger(ExecutableHipChatAction.class, settings));
|
||||
this.templateEngine = templateEngine;
|
||||
this.pagerDutyService = pagerDutyService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return PagerDutyAction.TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagerDutyAction parseAction(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
PagerDutyAction action = PagerDutyAction.parse(watchId, actionId, parser);
|
||||
PagerDutyAccount account = pagerDutyService.getAccount(action.event.account);
|
||||
if (account == null) {
|
||||
throw new ElasticsearchParseException("could not parse [pagerduty] action [{}/{}]. unknown pager duty account [{}]", watchId, account, action.event.account);
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutablePagerDutyAction createExecutable(PagerDutyAction action) {
|
||||
return new ExecutablePagerDutyAction(action, actionLogger, pagerDutyService, templateEngine);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.support.http.HttpMethod;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.Scheme;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Official documentation for this can be found at
|
||||
*
|
||||
* https://developer.pagerduty.com/documentation/howto/manually-trigger-an-incident/
|
||||
* https://developer.pagerduty.com/documentation/integration/events/trigger
|
||||
* https://developer.pagerduty.com/documentation/integration/events/acknowledge
|
||||
* https://developer.pagerduty.com/documentation/integration/events/resolve
|
||||
*/
|
||||
public class IncidentEvent implements ToXContent {
|
||||
|
||||
static final String HOST = "events.pagerduty.com";
|
||||
static final String PATH = "/generic/2010-04-15/create_event.json";
|
||||
|
||||
final String description;
|
||||
final @Nullable String incidentKey;
|
||||
final @Nullable String client;
|
||||
final @Nullable String clientUrl;
|
||||
final @Nullable String account;
|
||||
final String eventType;
|
||||
final boolean attachPayload;
|
||||
final @Nullable IncidentEventContext[] contexts;
|
||||
|
||||
public IncidentEvent(String description, @Nullable String eventType, @Nullable String incidentKey, @Nullable String client,
|
||||
@Nullable String clientUrl, @Nullable String account, boolean attachPayload, @Nullable IncidentEventContext[] contexts) {
|
||||
this.description = description;
|
||||
if (description == null) {
|
||||
throw new IllegalStateException("could not create pagerduty event. missing required [" + XField.DESCRIPTION.getPreferredName() + "] setting");
|
||||
}
|
||||
this.incidentKey = incidentKey;
|
||||
this.client = client;
|
||||
this.clientUrl = clientUrl;
|
||||
this.account = account;
|
||||
this.attachPayload = attachPayload;
|
||||
this.contexts = contexts;
|
||||
this.eventType = Strings.hasLength(eventType) ? eventType : "trigger";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
IncidentEvent template = (IncidentEvent) o;
|
||||
return Objects.equals(description, template.description) &&
|
||||
Objects.equals(incidentKey, template.incidentKey) &&
|
||||
Objects.equals(client, template.client) &&
|
||||
Objects.equals(clientUrl, template.clientUrl) &&
|
||||
Objects.equals(attachPayload, template.attachPayload) &&
|
||||
Objects.equals(eventType, template.eventType) &&
|
||||
Objects.equals(account, template.account) &&
|
||||
Arrays.equals(contexts, template.contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(description, incidentKey, client, clientUrl, account, attachPayload, eventType);
|
||||
result = 31 * result + Arrays.hashCode(contexts);
|
||||
return result;
|
||||
}
|
||||
|
||||
public HttpRequest createRequest(final String serviceKey, final Payload payload) throws IOException {
|
||||
return HttpRequest.builder(HOST, -1)
|
||||
.method(HttpMethod.POST)
|
||||
.scheme(Scheme.HTTPS)
|
||||
.path(PATH)
|
||||
.jsonBody(new ToXContent() {
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.field(XField.SERVICE_KEY.getPreferredName(), serviceKey);
|
||||
builder.field(XField.EVENT_TYPE.getPreferredName(), eventType);
|
||||
builder.field(XField.DESCRIPTION.getPreferredName(), description);
|
||||
if (incidentKey != null) {
|
||||
builder.field(XField.INCIDENT_KEY.getPreferredName(), incidentKey);
|
||||
}
|
||||
if (client != null) {
|
||||
builder.field(XField.CLIENT.getPreferredName(), client);
|
||||
}
|
||||
if (clientUrl != null) {
|
||||
builder.field(XField.CLIENT_URL.getPreferredName(), clientUrl);
|
||||
}
|
||||
if (attachPayload) {
|
||||
builder.startObject(XField.DETAILS.getPreferredName());
|
||||
builder.field(XField.PAYLOAD.getPreferredName());
|
||||
payload.toXContent(builder, params);
|
||||
builder.endObject();
|
||||
}
|
||||
if (contexts != null && contexts.length > 0) {
|
||||
builder.startArray(IncidentEvent.XField.CONTEXT.getPreferredName());
|
||||
for (IncidentEventContext context : contexts) {
|
||||
context.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(XField.DESCRIPTION.getPreferredName(), description);
|
||||
if (incidentKey != null) {
|
||||
builder.field(XField.INCIDENT_KEY.getPreferredName(), incidentKey);
|
||||
}
|
||||
if (client != null) {
|
||||
builder.field(XField.CLIENT.getPreferredName(), client);
|
||||
}
|
||||
if (clientUrl != null) {
|
||||
builder.field(XField.CLIENT_URL.getPreferredName(), clientUrl);
|
||||
}
|
||||
if (account != null) {
|
||||
builder.field(XField.ACCOUNT.getPreferredName(), account);
|
||||
}
|
||||
builder.field(XField.ATTACH_PAYLOAD.getPreferredName(), attachPayload);
|
||||
if (contexts != null) {
|
||||
builder.startArray(XField.CONTEXT.getPreferredName());
|
||||
for (IncidentEventContext context : contexts) {
|
||||
context.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
public static Template.Builder templateBuilder(String description) {
|
||||
return templateBuilder(TextTemplate.inline(description).build());
|
||||
}
|
||||
|
||||
public static Template.Builder templateBuilder(TextTemplate description) {
|
||||
return new Template.Builder(description);
|
||||
}
|
||||
|
||||
public static class Template implements ToXContent {
|
||||
|
||||
final TextTemplate description;
|
||||
final TextTemplate incidentKey;
|
||||
final TextTemplate client;
|
||||
final TextTemplate clientUrl;
|
||||
final TextTemplate eventType;
|
||||
public final String account;
|
||||
final Boolean attachPayload;
|
||||
final IncidentEventContext.Template[] contexts;
|
||||
|
||||
public Template(TextTemplate description, TextTemplate eventType, TextTemplate incidentKey, TextTemplate client, TextTemplate clientUrl, String account, Boolean attachPayload, IncidentEventContext.Template[] contexts) {
|
||||
this.description = description;
|
||||
this.eventType = eventType;
|
||||
this.incidentKey = incidentKey;
|
||||
this.client = client;
|
||||
this.clientUrl = clientUrl;
|
||||
this.account = account;
|
||||
this.attachPayload = attachPayload;
|
||||
this.contexts = contexts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Template template = (Template) o;
|
||||
return Objects.equals(description, template.description) &&
|
||||
Objects.equals(incidentKey, template.incidentKey) &&
|
||||
Objects.equals(client, template.client) &&
|
||||
Objects.equals(clientUrl, template.clientUrl) &&
|
||||
Objects.equals(eventType, template.eventType) &&
|
||||
Objects.equals(attachPayload, template.attachPayload) &&
|
||||
Objects.equals(account, template.account) &&
|
||||
Arrays.equals(contexts, template.contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(description, eventType, incidentKey, client, clientUrl, attachPayload, account);
|
||||
result = 31 * result + Arrays.hashCode(contexts);
|
||||
return result;
|
||||
}
|
||||
|
||||
public IncidentEvent render(String watchId, String actionId, TextTemplateEngine engine, Map<String, Object> model, IncidentEventDefaults defaults) {
|
||||
String description = this.description != null ? engine.render(this.description, model) : defaults.description;
|
||||
String incidentKey = this.incidentKey != null ? engine.render(this.incidentKey, model) :
|
||||
defaults.incidentKey != null ? defaults.incidentKey : watchId;
|
||||
String client = this.client != null ? engine.render(this.client, model) : defaults.client;
|
||||
String clientUrl = this.clientUrl != null ? engine.render(this.clientUrl, model) : defaults.clientUrl;
|
||||
String eventType = this.eventType != null ? engine.render(this.eventType, model) : defaults.eventType;
|
||||
boolean attachPayload = this.attachPayload != null ? this.attachPayload : defaults.attachPayload;
|
||||
IncidentEventContext[] contexts = null;
|
||||
if (this.contexts != null) {
|
||||
contexts = new IncidentEventContext[this.contexts.length];
|
||||
for (int i = 0; i < this.contexts.length; i++) {
|
||||
contexts[i] = this.contexts[i].render(engine, model, defaults);
|
||||
}
|
||||
}
|
||||
return new IncidentEvent(description, eventType, incidentKey, client, clientUrl, account, attachPayload, contexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(XField.DESCRIPTION.getPreferredName(), description, params);
|
||||
if (incidentKey != null) {
|
||||
builder.field(XField.INCIDENT_KEY.getPreferredName(), incidentKey, params);
|
||||
}
|
||||
if (client != null) {
|
||||
builder.field(XField.CLIENT.getPreferredName(), client, params);
|
||||
}
|
||||
if (clientUrl != null) {
|
||||
builder.field(XField.CLIENT_URL.getPreferredName(), clientUrl, params);
|
||||
}
|
||||
if (eventType != null) {
|
||||
builder.field(XField.EVENT_TYPE.getPreferredName(), eventType, params);
|
||||
}
|
||||
if (attachPayload != null) {
|
||||
builder.field(XField.ATTACH_PAYLOAD.getPreferredName(), attachPayload);
|
||||
}
|
||||
if (account != null) {
|
||||
builder.field(XField.ACCOUNT.getPreferredName(), account);
|
||||
}
|
||||
if (contexts != null) {
|
||||
builder.startArray(XField.CONTEXT.getPreferredName());
|
||||
for (IncidentEventContext.Template context : contexts) {
|
||||
context.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static Template parse(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
TextTemplate incidentKey = null;
|
||||
TextTemplate description = null;
|
||||
TextTemplate client = null;
|
||||
TextTemplate clientUrl = null;
|
||||
TextTemplate eventType = null;
|
||||
String account = null;
|
||||
Boolean attachPayload = null;
|
||||
IncidentEventContext.Template[] contexts = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.INCIDENT_KEY)) {
|
||||
try {
|
||||
incidentKey = TextTemplate.parse(parser);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}]", XField.INCIDENT_KEY.getPreferredName());
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.DESCRIPTION)) {
|
||||
try {
|
||||
description = TextTemplate.parse(parser);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}]", XField.DESCRIPTION.getPreferredName());
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.CLIENT)) {
|
||||
try {
|
||||
client = TextTemplate.parse(parser);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}]", XField.CLIENT.getPreferredName());
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.CLIENT_URL)) {
|
||||
try {
|
||||
clientUrl = TextTemplate.parse(parser);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}]", XField.CLIENT_URL.getPreferredName());
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.ACCOUNT)) {
|
||||
try {
|
||||
account = parser.text();
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}]", XField.CLIENT_URL.getPreferredName());
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.EVENT_TYPE)) {
|
||||
try {
|
||||
eventType = TextTemplate.parse(parser);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}]", XField.EVENT_TYPE.getPreferredName());
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.ATTACH_PAYLOAD)) {
|
||||
if (token == XContentParser.Token.VALUE_BOOLEAN) {
|
||||
attachPayload = parser.booleanValue();
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}], expected a boolean value but found [{}] instead", XField.ATTACH_PAYLOAD.getPreferredName(), token);
|
||||
}
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.CONTEXT)) {
|
||||
if (token == XContentParser.Token.START_ARRAY) {
|
||||
List<IncidentEventContext.Template> list = new ArrayList<>();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
try {
|
||||
list.add(IncidentEventContext.Template.parse(parser));
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. failed to parse field [{}]", XField.CONTEXT.getPreferredName());
|
||||
}
|
||||
}
|
||||
contexts = list.toArray(new IncidentEventContext.Template[list.size()]);
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse pager duty event template. unexpected field [{}]", currentFieldName);
|
||||
}
|
||||
}
|
||||
return new Template(description, eventType, incidentKey, client, clientUrl, account, attachPayload, contexts);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
final TextTemplate description;
|
||||
TextTemplate incidentKey;
|
||||
TextTemplate client;
|
||||
TextTemplate clientUrl;
|
||||
TextTemplate eventType;
|
||||
String account;
|
||||
Boolean attachPayload;
|
||||
List<IncidentEventContext.Template> contexts = new ArrayList<>();
|
||||
|
||||
public Builder(TextTemplate description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Builder setIncidentKey(TextTemplate incidentKey) {
|
||||
this.incidentKey = incidentKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setClient(TextTemplate client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setClientUrl(TextTemplate clientUrl) {
|
||||
this.clientUrl = clientUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEventType(TextTemplate eventType) {
|
||||
this.eventType = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAccount(String account) {
|
||||
this.account= account;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAttachPayload(Boolean attachPayload) {
|
||||
this.attachPayload = attachPayload;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addContext(IncidentEventContext.Template context) {
|
||||
this.contexts.add(context);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Template build() {
|
||||
IncidentEventContext.Template[] contexts = this.contexts.isEmpty() ? null :
|
||||
this.contexts.toArray(new IncidentEventContext.Template[this.contexts.size()]);
|
||||
return new Template(description, eventType, incidentKey, client, clientUrl, account, attachPayload, contexts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface XField {
|
||||
|
||||
ParseField TYPE = new ParseField("type");
|
||||
ParseField EVENT_TYPE = new ParseField("event_type");
|
||||
|
||||
ParseField ACCOUNT = new ParseField("account");
|
||||
ParseField DESCRIPTION = new ParseField("description");
|
||||
ParseField INCIDENT_KEY = new ParseField("incident_key");
|
||||
ParseField CLIENT = new ParseField("client");
|
||||
ParseField CLIENT_URL = new ParseField("client_url");
|
||||
ParseField ATTACH_PAYLOAD = new ParseField("attach_payload");
|
||||
ParseField CONTEXT = new ParseField("context");
|
||||
|
||||
ParseField SERVICE_KEY = new ParseField("service_key");
|
||||
ParseField PAYLOAD = new ParseField("payload");
|
||||
ParseField DETAILS = new ParseField("details");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IncidentEventContext implements ToXContent {
|
||||
|
||||
enum Type {
|
||||
LINK, IMAGE
|
||||
}
|
||||
|
||||
final Type type;
|
||||
final String href;
|
||||
final String text;
|
||||
final String src;
|
||||
final String alt;
|
||||
|
||||
public static IncidentEventContext link(String href, @Nullable String text) {
|
||||
assert href != null;
|
||||
return new IncidentEventContext(Type.LINK, href, text, null, null);
|
||||
}
|
||||
|
||||
public static IncidentEventContext image(String src, @Nullable String href, @Nullable String alt) {
|
||||
assert src != null;
|
||||
return new IncidentEventContext(Type.IMAGE, href, null, src, alt);
|
||||
}
|
||||
|
||||
private IncidentEventContext(Type type, String href, String text, String src, String alt) {
|
||||
this.type = type;
|
||||
this.href = href;
|
||||
this.text = text;
|
||||
this.src = src;
|
||||
this.alt = alt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
IncidentEventContext that = (IncidentEventContext) o;
|
||||
|
||||
return Objects.equals(type, that.type) && Objects.equals(href, that.href) && Objects.equals(text, that.text) && Objects.equals(src, that.src)
|
||||
&& Objects.equals(alt, that.alt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, href, text, src, alt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(XField.TYPE.getPreferredName(), type.name().toLowerCase(Locale.ROOT));
|
||||
switch (type) {
|
||||
case LINK:
|
||||
builder.field(XField.HREF.getPreferredName(), href);
|
||||
if (text != null) {
|
||||
builder.field(XField.TEXT.getPreferredName(), text);
|
||||
}
|
||||
break;
|
||||
case IMAGE:
|
||||
builder.field(XField.SRC.getPreferredName(), src);
|
||||
if (href != null) {
|
||||
builder.field(XField.HREF.getPreferredName(), href);
|
||||
}
|
||||
if (alt != null) {
|
||||
builder.field(XField.ALT.getPreferredName(), alt);
|
||||
}
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static class Template implements ToXContent {
|
||||
|
||||
final Type type;
|
||||
final TextTemplate href;
|
||||
final TextTemplate text;
|
||||
final TextTemplate src;
|
||||
final TextTemplate alt;
|
||||
|
||||
public static Template link(TextTemplate href, @Nullable TextTemplate text) {
|
||||
if (href == null) {
|
||||
throw new IllegalStateException("could not create link context for pager duty trigger incident event. missing required [href] setting");
|
||||
}
|
||||
return new Template(Type.LINK, href, text, null, null);
|
||||
}
|
||||
|
||||
public static Template image(TextTemplate src, @Nullable TextTemplate href, @Nullable TextTemplate alt) {
|
||||
if (src == null) {
|
||||
throw new IllegalStateException("could not create link context for pager duty trigger incident event. missing required [src] setting");
|
||||
}
|
||||
return new Template(Type.IMAGE, href, null, src, alt);
|
||||
}
|
||||
|
||||
private Template(Type type, TextTemplate href, TextTemplate text, TextTemplate src, TextTemplate alt) {
|
||||
this.type = type;
|
||||
this.href = href;
|
||||
this.text = text;
|
||||
this.src = src;
|
||||
this.alt = alt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Template that = (Template) o;
|
||||
return Objects.equals(type, that.type) && Objects.equals(href, that.href) && Objects.equals(text, that.text) && Objects.equals(src, that.src)
|
||||
&& Objects.equals(alt, that.alt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, href, text, src, alt);
|
||||
}
|
||||
|
||||
public IncidentEventContext render(TextTemplateEngine engine, Map<String, Object> model, IncidentEventDefaults defaults) {
|
||||
switch (type) {
|
||||
case LINK:
|
||||
String href = this.href != null ? engine.render(this.href, model) : defaults.link.href;
|
||||
String text = this.text != null ? engine.render(this.text, model) : defaults.link.text;
|
||||
return IncidentEventContext.link(href, text);
|
||||
|
||||
default:
|
||||
assert type == Type.IMAGE;
|
||||
String src = this.src != null ? engine.render(this.src, model) : defaults.image.src;
|
||||
href = this.href != null ? engine.render(this.href, model) : defaults.image.href;
|
||||
String alt = this.alt != null ? engine.render(this.alt, model) : defaults.image.alt;
|
||||
return IncidentEventContext.image(src, href, alt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(XField.TYPE.getPreferredName(), type.name().toLowerCase(Locale.ROOT));
|
||||
switch (type) {
|
||||
case LINK:
|
||||
builder.field(XField.HREF.getPreferredName(), href, params);
|
||||
if (text != null) {
|
||||
builder.field(XField.TEXT.getPreferredName(), text, params);
|
||||
}
|
||||
break;
|
||||
case IMAGE:
|
||||
builder.field(XField.SRC.getPreferredName(), src, params);
|
||||
if (href != null) {
|
||||
builder.field(XField.HREF.getPreferredName(), href, params);
|
||||
}
|
||||
if (alt != null) {
|
||||
builder.field(XField.ALT.getPreferredName(), alt, params);
|
||||
}
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static Template parse(XContentParser parser) throws IOException {
|
||||
Type type = null;
|
||||
TextTemplate href = null;
|
||||
TextTemplate text = null;
|
||||
TextTemplate src = null;
|
||||
TextTemplate alt = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Strings.hasLength(currentFieldName)) {
|
||||
if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.TYPE)) {
|
||||
try {
|
||||
type = Type.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. unknown context type [{}]", parser.text());
|
||||
}
|
||||
} else {
|
||||
TextTemplate parsedTemplate;
|
||||
try {
|
||||
parsedTemplate = TextTemplate.parse(parser);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. failed to parse [{}] field", e, parser.text(), currentFieldName);
|
||||
}
|
||||
|
||||
if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.HREF)) {
|
||||
href = parsedTemplate;
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.TEXT)) {
|
||||
text = parsedTemplate;
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.SRC)) {
|
||||
src = parsedTemplate;
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.ALT)) {
|
||||
alt = parsedTemplate;
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. unknown field [{}]", currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return createAndValidateTemplate(type, href, src, alt, text);
|
||||
}
|
||||
|
||||
private static Template createAndValidateTemplate(Type type, TextTemplate href, TextTemplate src, TextTemplate alt, TextTemplate text) {
|
||||
if (type == null) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. missing required field [{}]", XField.TYPE.getPreferredName());
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case LINK:
|
||||
if (href == null) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. missing required field [{}] for [{}] context", XField.HREF.getPreferredName(), Type.LINK.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
if (src != null) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. unexpected field [{}] for [{}] context", XField.SRC.getPreferredName(), Type.LINK.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
if (alt != null) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. unexpected field [{}] for [{}] context", XField.ALT.getPreferredName(), Type.LINK.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return link(href, text);
|
||||
case IMAGE:
|
||||
if (src == null) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. missing required field [{}] for [{}] context", XField.SRC.getPreferredName(), Type.IMAGE.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
if (text != null) {
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. unexpected field [{}] for [{}] context", XField.TEXT.getPreferredName(), Type.IMAGE.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return image(src, href, alt);
|
||||
default:
|
||||
throw new ElasticsearchParseException("could not parse trigger incident event context. unknown context type [{}]", type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface XField {
|
||||
ParseField TYPE = new ParseField("type");
|
||||
ParseField HREF = new ParseField("href");
|
||||
|
||||
// "link" context fields
|
||||
ParseField TEXT = new ParseField("text");
|
||||
|
||||
// "image" context fields
|
||||
ParseField SRC = new ParseField("src");
|
||||
ParseField ALT = new ParseField("alt");
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Get trigger default configurations either from global settings or specific account settings and merge them
|
||||
*/
|
||||
public class IncidentEventDefaults {
|
||||
|
||||
final String description;
|
||||
final String incidentKey;
|
||||
final String client;
|
||||
final String clientUrl;
|
||||
final String eventType;
|
||||
final boolean attachPayload;
|
||||
final Context.LinkDefaults link;
|
||||
final Context.ImageDefaults image;
|
||||
|
||||
public IncidentEventDefaults(Settings accountSettings) {
|
||||
description = accountSettings.get(IncidentEvent.XField.DESCRIPTION.getPreferredName(), null);
|
||||
incidentKey = accountSettings.get(IncidentEvent.XField.INCIDENT_KEY.getPreferredName(), null);
|
||||
client = accountSettings.get(IncidentEvent.XField.CLIENT.getPreferredName(), null);
|
||||
clientUrl = accountSettings.get(IncidentEvent.XField.CLIENT_URL.getPreferredName(), null);
|
||||
eventType = accountSettings.get(IncidentEvent.XField.EVENT_TYPE.getPreferredName(), null);
|
||||
attachPayload = accountSettings.getAsBoolean(IncidentEvent.XField.ATTACH_PAYLOAD.getPreferredName(), false);
|
||||
link = new Context.LinkDefaults(accountSettings.getAsSettings("link"));
|
||||
image = new Context.ImageDefaults(accountSettings.getAsSettings("image"));
|
||||
|
||||
}
|
||||
|
||||
static class Context {
|
||||
|
||||
static class LinkDefaults {
|
||||
|
||||
final String href;
|
||||
final String text;
|
||||
|
||||
public LinkDefaults(Settings settings) {
|
||||
href = settings.get(IncidentEventContext.XField.HREF.getPreferredName(), null);
|
||||
text = settings.get(IncidentEventContext.XField.TEXT.getPreferredName(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(href, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || getClass() != obj.getClass()){
|
||||
return false;
|
||||
}
|
||||
final LinkDefaults other = (LinkDefaults) obj;
|
||||
return Objects.equals(href, other.href) && Objects.equals(text, other.text);
|
||||
}
|
||||
}
|
||||
|
||||
static class ImageDefaults {
|
||||
|
||||
final String href;
|
||||
final String src;
|
||||
final String alt;
|
||||
|
||||
public ImageDefaults(Settings settings) {
|
||||
href = settings.get(IncidentEventContext.XField.HREF.getPreferredName(), null);
|
||||
src = settings.get(IncidentEventContext.XField.SRC.getPreferredName(), null);
|
||||
alt = settings.get(IncidentEventContext.XField.ALT.getPreferredName(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(href, src, alt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || getClass() != obj.getClass()){
|
||||
return false;
|
||||
}
|
||||
final ImageDefaults other = (ImageDefaults) obj;
|
||||
return Objects.equals(href, other.href) && Objects.equals(src, other.src) && Objects.equals(alt, other.alt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.ClusterSettings;
|
||||
import org.elasticsearch.common.settings.Setting;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.watcher.shield.WatcherSettingsFilter;
|
||||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class InternalPagerDutyService extends AbstractLifecycleComponent<PagerDutyService> implements PagerDutyService {
|
||||
|
||||
public static final Setting<Settings> PAGERDUTY_ACCOUNT_SETTING = Setting.groupSetting("watcher.actions.pagerduty.service.", true, Setting.Scope.CLUSTER);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private volatile PagerDutyAccounts accounts;
|
||||
|
||||
@Inject
|
||||
public InternalPagerDutyService(Settings settings, HttpClient httpClient, ClusterSettings clusterSettings,
|
||||
WatcherSettingsFilter settingsFilter) {
|
||||
super(settings);
|
||||
this.httpClient = httpClient;
|
||||
settingsFilter.filterOut(
|
||||
"watcher.actions.pagerduty.service." + PagerDutyAccount.SERVICE_KEY_SETTING,
|
||||
"watcher.actions.pagerduty.service.account.*." + PagerDutyAccount.SERVICE_KEY_SETTING
|
||||
);
|
||||
clusterSettings.addSettingsUpdateConsumer(PAGERDUTY_ACCOUNT_SETTING, this::setPagerDutyAccountSetting);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
setPagerDutyAccountSetting(PAGERDUTY_ACCOUNT_SETTING.get(settings));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() {
|
||||
}
|
||||
|
||||
private void setPagerDutyAccountSetting(Settings settings) {
|
||||
accounts = new PagerDutyAccounts(settings, httpClient, logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagerDutyAccount getDefaultAccount() {
|
||||
return accounts.account(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagerDutyAccount getAccount(String name) {
|
||||
return accounts.account(name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.settings.SettingsException;
|
||||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpResponse;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PagerDutyAccount {
|
||||
|
||||
public static final String SERVICE_KEY_SETTING = "service_api_key";
|
||||
public static final String TRIGGER_DEFAULTS_SETTING = "event_defaults";
|
||||
|
||||
final String name;
|
||||
final String serviceKey;
|
||||
final HttpClient httpClient;
|
||||
final IncidentEventDefaults eventDefaults;
|
||||
final ESLogger logger;
|
||||
|
||||
public PagerDutyAccount(String name, Settings accountSettings, Settings serviceSettings, HttpClient httpClient, ESLogger logger) {
|
||||
this.name = name;
|
||||
this.serviceKey = accountSettings.get(SERVICE_KEY_SETTING, serviceSettings.get(SERVICE_KEY_SETTING, null));
|
||||
if (this.serviceKey == null) {
|
||||
throw new SettingsException("invalid pagerduty account [" + name + "]. missing required [" + SERVICE_KEY_SETTING + "] setting");
|
||||
}
|
||||
this.httpClient = httpClient;
|
||||
|
||||
this.eventDefaults = new IncidentEventDefaults(accountSettings.getAsSettings(TRIGGER_DEFAULTS_SETTING));
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public IncidentEventDefaults getDefaults() {
|
||||
return eventDefaults;
|
||||
}
|
||||
|
||||
public SentEvent send(IncidentEvent event, Payload payload) throws IOException {
|
||||
HttpRequest request = event.createRequest(serviceKey, payload);
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
return SentEvent.responded(event, request, response);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.settings.SettingsException;
|
||||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PagerDutyAccounts {
|
||||
|
||||
private final Map<String, PagerDutyAccount> accounts;
|
||||
private final String defaultAccountName;
|
||||
|
||||
public PagerDutyAccounts(Settings serviceSettings, HttpClient httpClient, ESLogger logger) {
|
||||
Settings accountsSettings = serviceSettings.getAsSettings("account");
|
||||
accounts = new HashMap<>();
|
||||
for (String name : accountsSettings.names()) {
|
||||
Settings accountSettings = accountsSettings.getAsSettings(name);
|
||||
PagerDutyAccount account = new PagerDutyAccount(name, accountSettings, serviceSettings, httpClient, logger);
|
||||
accounts.put(name, account);
|
||||
}
|
||||
|
||||
String defaultAccountName = serviceSettings.get("default_account");
|
||||
if (defaultAccountName == null) {
|
||||
if (accounts.isEmpty()) {
|
||||
this.defaultAccountName = null;
|
||||
} else {
|
||||
PagerDutyAccount account = accounts.values().iterator().next();
|
||||
logger.info("default pager duty account set to [{}]", account.name);
|
||||
this.defaultAccountName = account.name;
|
||||
}
|
||||
} else if (!accounts.containsKey(defaultAccountName)) {
|
||||
throw new SettingsException("could not find default pagerduty account [" + defaultAccountName + "]");
|
||||
} else {
|
||||
this.defaultAccountName = defaultAccountName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the account associated with the given name. If there is not such account, {@code null} is returned.
|
||||
* If the given name is {@code null}, the default account will be returned.
|
||||
*
|
||||
* @param name The name of the requested account
|
||||
* @return The account associated with the given name, or {@code null} when requested an unknown account.
|
||||
* @throws IllegalStateException if the name is null and the default account is null.
|
||||
*/
|
||||
public PagerDutyAccount account(String name) throws IllegalStateException {
|
||||
if (name == null) {
|
||||
if (defaultAccountName == null) {
|
||||
throw new IllegalStateException("cannot find default pagerduty account as no accounts have been configured");
|
||||
}
|
||||
name = defaultAccountName;
|
||||
}
|
||||
return accounts.get(name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.common.component.LifecycleComponent;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public interface PagerDutyService extends LifecycleComponent<PagerDutyService> {
|
||||
|
||||
PagerDutyAccount getDefaultAccount();
|
||||
|
||||
PagerDutyAccount getAccount(String accountName);
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.PagerDutyAction;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SentEvent implements ToXContent {
|
||||
|
||||
final IncidentEvent event;
|
||||
final @Nullable HttpRequest request;
|
||||
final @Nullable HttpResponse response;
|
||||
final @Nullable String failureReason;
|
||||
|
||||
public static SentEvent responded(IncidentEvent event, HttpRequest request, HttpResponse response) {
|
||||
String failureReason = resolveFailureReason(response);
|
||||
return new SentEvent(event, request, response, failureReason);
|
||||
}
|
||||
|
||||
public static SentEvent error(IncidentEvent event, String reason) {
|
||||
return new SentEvent(event, null, null, reason);
|
||||
}
|
||||
|
||||
private SentEvent(IncidentEvent event, HttpRequest request, HttpResponse response, String failureReason) {
|
||||
this.event = event;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.failureReason = failureReason;
|
||||
}
|
||||
|
||||
public boolean successful() {
|
||||
return failureReason == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
SentEvent sentEvent = (SentEvent) o;
|
||||
return Objects.equals(event, sentEvent.event) && Objects.equals(request, sentEvent.request) && Objects.equals(failureReason, sentEvent.failureReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(event, request, response, failureReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(XField.EVENT.getPreferredName(), event, params);
|
||||
if (!successful()) {
|
||||
builder.field(XField.REASON.getPreferredName(), failureReason);
|
||||
if (request != null) {
|
||||
builder.field(XField.REQUEST.getPreferredName(), request, params);
|
||||
}
|
||||
if (response != null) {
|
||||
builder.field(XField.RESPONSE.getPreferredName(), response, params);
|
||||
}
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
private static String resolveFailureReason(HttpResponse response) {
|
||||
|
||||
// if for some reason we failed to parse the body, lets fall back on the http status code.
|
||||
int status = response.status();
|
||||
if (status < 300) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ok... we have an error
|
||||
|
||||
// lets first try to parse the error response in the body
|
||||
// based on https://developer.pagerduty.com/documentation/rest/errors
|
||||
try {
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(response.body());
|
||||
parser.nextToken();
|
||||
|
||||
String message = null;
|
||||
List<String> errors = new ArrayList<>();
|
||||
|
||||
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 (ParseFieldMatcher.STRICT.match(currentFieldName, XField.MESSAGE)) {
|
||||
message = parser.text();
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.CODE)) {
|
||||
// we don't use this code.. so just consume the token
|
||||
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, XField.ERRORS)) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
errors.add(parser.text());
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse pagerduty event response. unexpected field [{}]", currentFieldName);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (message != null) {
|
||||
sb.append(message);
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
sb.append(":");
|
||||
for (String error : errors) {
|
||||
sb.append(" ").append(error).append(".");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
// too bad... we couldn't parse the body... note that we don't log this error as there's no real
|
||||
// need for it. This whole error parsing is a nice to have, nothing more. On error, the http
|
||||
// response object is anyway added to the action result in the watch record (though not searchable)
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 400: return "Bad Request";
|
||||
case 401: return "Unauthorized. The account service api key is invalid.";
|
||||
case 403: return "Forbidden. The account doesn't have permission to send this trigger.";
|
||||
case 404: return "The account used invalid HipChat APIs";
|
||||
case 408: return "Request Timeout. The request took too long to process.";
|
||||
case 500: return "PagerDuty Server Error. Internal error occurred while processing request.";
|
||||
default:
|
||||
return "Unknown Error";
|
||||
}
|
||||
}
|
||||
|
||||
public interface XField {
|
||||
ParseField EVENT = PagerDutyAction.XField.EVENT;
|
||||
ParseField REASON = new ParseField("reason");
|
||||
ParseField REQUEST = new ParseField("request");
|
||||
ParseField RESPONSE = new ParseField("response");
|
||||
|
||||
ParseField MESSAGE = new ParseField("message");
|
||||
ParseField CODE = new ParseField("code");
|
||||
ParseField ERRORS = new ParseField("errors");
|
||||
}
|
||||
}
|
|
@ -56,6 +56,4 @@ public class InternalSlackService extends AbstractLifecycleComponent<SlackServic
|
|||
public SlackAccount getAccount(String name) {
|
||||
return accounts.account(name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -451,6 +451,87 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"pagerduty" : {
|
||||
"type": "object",
|
||||
"dynamic": true,
|
||||
"properties": {
|
||||
"account": {
|
||||
"type": "string",
|
||||
"index": "not_analyzed"
|
||||
},
|
||||
"sent_event": {
|
||||
"type": "nested",
|
||||
"include_in_parent": true,
|
||||
"dynamic": true,
|
||||
"properties": {
|
||||
"reason": {
|
||||
"type": "string"
|
||||
},
|
||||
"request" : {
|
||||
"type" : "object",
|
||||
"enabled" : false
|
||||
},
|
||||
"response" : {
|
||||
"type" : "object",
|
||||
"enabled" : false
|
||||
},
|
||||
"event" : {
|
||||
"type" : "object",
|
||||
"dynamic" : true,
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"client" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"client_url" : {
|
||||
"index" : "not_analyzed",
|
||||
"type" : "string"
|
||||
},
|
||||
"account" : {
|
||||
"index" : "not_analyzed",
|
||||
"type" : "string"
|
||||
},
|
||||
"attach_payload" : {
|
||||
"type" : "boolean"
|
||||
},
|
||||
"incident_key" : {
|
||||
"index" : "not_analyzed",
|
||||
"type" : "string"
|
||||
},
|
||||
"description" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"context" : {
|
||||
"type" : "nested",
|
||||
"include_in_parent": true,
|
||||
"dynamic" : true,
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"href" : {
|
||||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"src" : {
|
||||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"alt" : {
|
||||
"type" : "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,4 +544,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,15 @@ public class WatcherF {
|
|||
settings.put("watcher.actions.hipchat.service.account.user.profile", "user");
|
||||
settings.put("watcher.actions.hipchat.service.account.user.auth_token", "FYVx16oDH78ZW9r13wtXbcszyoyA7oX5tiMWg9X0");
|
||||
|
||||
// this is for the `test-watcher-v1` notification token
|
||||
// this is for the `test-watcher-v1` notification token (hipchat)
|
||||
settings.put("watcher.actions.hipchat.service.account.v1.profile", "v1");
|
||||
settings.put("watcher.actions.hipchat.service.account.v1.auth_token", "a734baf62df618b96dda55b323fc30");
|
||||
|
||||
// this is for our test slack incoming webhook (under elasticsearch team)
|
||||
System.setProperty("es.watcher.actions.slack.service.account.a1.url", "https://hooks.slack.com/services/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");
|
||||
|
||||
System.setProperty("es.watcher.actions.pagerduty.service.account.service1.service_api_key", "fc082467005d4072a914e0bb041882d0");
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final Node node = new MockNode(settings.build(), Version.CURRENT, Arrays.asList(XPackPlugin.class, XPackPlugin.class));
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyAccount;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyService;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.junit.Before;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.watcher.actions.ActionBuilders.triggerPagerDutyAction;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PagerDutyActionFactoryTests extends ESTestCase {
|
||||
|
||||
private PagerDutyActionFactory factory;
|
||||
private PagerDutyService service;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
service = mock(PagerDutyService.class);
|
||||
factory = new PagerDutyActionFactory(Settings.EMPTY, mock(TextTemplateEngine.class), service);
|
||||
}
|
||||
|
||||
public void testParseAction() throws Exception {
|
||||
|
||||
PagerDutyAccount account = mock(PagerDutyAccount.class);
|
||||
when(service.getAccount("_account1")).thenReturn(account);
|
||||
|
||||
PagerDutyAction action = triggerPagerDutyAction("_account1", "_description").build();
|
||||
XContentBuilder jsonBuilder = jsonBuilder().value(action);
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(jsonBuilder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
PagerDutyAction parsedAction = factory.parseAction("_w1", "_a1", parser);
|
||||
assertThat(parsedAction, is(action));
|
||||
}
|
||||
|
||||
public void testParseActionUnknownAccount() throws Exception {
|
||||
try {
|
||||
when(service.getAccount("_unknown")).thenReturn(null);
|
||||
|
||||
PagerDutyAction action = triggerPagerDutyAction("_unknown", "_body").build();
|
||||
XContentBuilder jsonBuilder = jsonBuilder().value(action);
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(jsonBuilder.bytes());
|
||||
parser.nextToken();
|
||||
factory.parseAction("_w1", "_a1", parser);
|
||||
fail("Expected ElasticsearchParseException due to unknown account");
|
||||
} catch (ElasticsearchParseException e) {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.IncidentEvent;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.IncidentEventContext;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.IncidentEventDefaults;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyAccount;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.PagerDutyService;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.service.SentEvent;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest;
|
||||
import org.elasticsearch.watcher.support.http.HttpResponse;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplate;
|
||||
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.watcher.actions.ActionBuilders.pagerDutyAction;
|
||||
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContextBuilder;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PagerDutyActionTests extends ESTestCase {
|
||||
|
||||
private PagerDutyService service;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
service = mock(PagerDutyService.class);
|
||||
}
|
||||
|
||||
public void testExecute() throws Exception {
|
||||
final String accountName = "account1";
|
||||
|
||||
TextTemplateEngine templateEngine = mock(TextTemplateEngine.class);
|
||||
|
||||
TextTemplate description = TextTemplate.inline("_description").build();
|
||||
IncidentEvent.Template.Builder eventBuilder = new IncidentEvent.Template.Builder(description);
|
||||
boolean attachPayload = randomBoolean();
|
||||
eventBuilder.setAttachPayload(attachPayload);
|
||||
eventBuilder.setAccount(accountName);
|
||||
IncidentEvent.Template eventTemplate = eventBuilder.build();
|
||||
|
||||
PagerDutyAction action = new PagerDutyAction(eventTemplate);
|
||||
ExecutablePagerDutyAction executable = new ExecutablePagerDutyAction(action, logger, service, templateEngine);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
Payload payload = new Payload.Simple(data);
|
||||
|
||||
Map<String, Object> metadata = MapBuilder.<String, Object>newMapBuilder().put("_key", "_val").map();
|
||||
|
||||
DateTime now = DateTime.now(DateTimeZone.UTC);
|
||||
|
||||
Wid wid = new Wid(randomAsciiOfLength(5), randomLong(), now);
|
||||
WatchExecutionContext ctx = mockExecutionContextBuilder(wid.watchId())
|
||||
.wid(wid)
|
||||
.payload(payload)
|
||||
.time(wid.watchId(), now)
|
||||
.metadata(metadata)
|
||||
.buildMock();
|
||||
|
||||
Map<String, Object> ctxModel = new HashMap<>();
|
||||
ctxModel.put("id", ctx.id().value());
|
||||
ctxModel.put("watch_id", wid.watchId());
|
||||
ctxModel.put("payload", data);
|
||||
ctxModel.put("metadata", metadata);
|
||||
ctxModel.put("execution_time", now);
|
||||
Map<String, Object> triggerModel = new HashMap<>();
|
||||
triggerModel.put("triggered_time", now);
|
||||
triggerModel.put("scheduled_time", now);
|
||||
ctxModel.put("trigger", triggerModel);
|
||||
ctxModel.put("vars", Collections.emptyMap());
|
||||
Map<String, Object> expectedModel = new HashMap<>();
|
||||
expectedModel.put("ctx", ctxModel);
|
||||
|
||||
when(templateEngine.render(description, expectedModel)).thenReturn(description.getTemplate());
|
||||
|
||||
IncidentEvent event = new IncidentEvent(description.getTemplate(), null, wid.watchId(), null, null, accountName, attachPayload, null);
|
||||
PagerDutyAccount account = mock(PagerDutyAccount.class);
|
||||
when(account.getDefaults()).thenReturn(new IncidentEventDefaults(Settings.EMPTY));
|
||||
HttpResponse response = mock(HttpResponse.class);
|
||||
when(response.status()).thenReturn(200);
|
||||
HttpRequest request = mock(HttpRequest.class);
|
||||
SentEvent sentEvent = SentEvent.responded(event, request, response);
|
||||
when(account.send(event, payload)).thenReturn(sentEvent);
|
||||
when(service.getAccount(accountName)).thenReturn(account);
|
||||
|
||||
Action.Result result = executable.execute("_id", ctx, payload);
|
||||
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result, instanceOf(PagerDutyAction.Result.Executed.class));
|
||||
assertThat(result.status(), equalTo(Action.Result.Status.SUCCESS));
|
||||
assertThat(((PagerDutyAction.Result.Executed) result).sentEvent(), sameInstance(sentEvent));
|
||||
}
|
||||
|
||||
public void testParser() throws Exception {
|
||||
|
||||
XContentBuilder builder = jsonBuilder().startObject();
|
||||
|
||||
String accountName = randomAsciiOfLength(10);
|
||||
builder.field("account", accountName);
|
||||
|
||||
TextTemplate incidentKey = null;
|
||||
if (randomBoolean()) {
|
||||
incidentKey = TextTemplate.inline("_incident_key").build();
|
||||
builder.field("incident_key", incidentKey);
|
||||
}
|
||||
|
||||
TextTemplate description = null;
|
||||
if (randomBoolean()) {
|
||||
description = TextTemplate.inline("_description").build();
|
||||
builder.field("description", description);
|
||||
}
|
||||
|
||||
TextTemplate client = null;
|
||||
if (randomBoolean()) {
|
||||
client = TextTemplate.inline("_client").build();
|
||||
builder.field("client", client);
|
||||
}
|
||||
|
||||
TextTemplate clientUrl = null;
|
||||
if (randomBoolean()) {
|
||||
clientUrl = TextTemplate.inline("_client_url").build();
|
||||
builder.field("client_url", clientUrl);
|
||||
}
|
||||
|
||||
TextTemplate eventType = null;
|
||||
if (randomBoolean()) {
|
||||
eventType = TextTemplate.inline(randomFrom("trigger", "resolve", "acknowledge")).build();
|
||||
builder.field("eventType", eventType);
|
||||
}
|
||||
|
||||
Boolean attachPayload = randomBoolean() ? null : randomBoolean();
|
||||
if (attachPayload != null) {
|
||||
builder.field("attach_payload", attachPayload.booleanValue());
|
||||
}
|
||||
|
||||
IncidentEventContext.Template[] contexts = null;
|
||||
if (randomBoolean()) {
|
||||
contexts = new IncidentEventContext.Template[] {
|
||||
IncidentEventContext.Template.link(TextTemplate.inline("_href").build(), TextTemplate.inline("_text").build()),
|
||||
IncidentEventContext.Template.image(TextTemplate.inline("_src").build(), TextTemplate.inline("_href").build(), TextTemplate.inline("_alt").build())
|
||||
};
|
||||
builder.array("context", (Object) contexts);
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
|
||||
BytesReference bytes = builder.bytes();
|
||||
logger.info("pagerduty action json [{}]", bytes.toUtf8());
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
|
||||
PagerDutyAction action = PagerDutyAction.parse("_watch", "_action", parser);
|
||||
|
||||
assertThat(action, notNullValue());
|
||||
assertThat(action.event.account, is(accountName));
|
||||
assertThat(action.event, notNullValue());
|
||||
assertThat(action.event, instanceOf(IncidentEvent.Template.class));
|
||||
assertThat(action.event, is(new IncidentEvent.Template(description, eventType, incidentKey, client, clientUrl, accountName, attachPayload, contexts)));
|
||||
}
|
||||
|
||||
public void testParserSelfGenerated() throws Exception {
|
||||
IncidentEvent.Template.Builder event = IncidentEvent.templateBuilder(randomAsciiOfLength(50));
|
||||
|
||||
if (randomBoolean()) {
|
||||
event.setIncidentKey(TextTemplate.inline(randomAsciiOfLength(50)).build());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
event.setClient(TextTemplate.inline(randomAsciiOfLength(50)).build());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
event.setClientUrl(TextTemplate.inline(randomAsciiOfLength(50)).build());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
event.setAttachPayload(randomBoolean());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
event.addContext(IncidentEventContext.Template.link(TextTemplate.inline("_href").build(), TextTemplate.inline("_text").build()));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
event.addContext(IncidentEventContext.Template.image(TextTemplate.inline("_src").build(), TextTemplate.inline("_href").build(), TextTemplate.inline("_alt").build()));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
event.setEventType(TextTemplate.inline(randomAsciiOfLength(50)).build());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
event.setAccount(randomAsciiOfLength(50)).build();
|
||||
}
|
||||
|
||||
PagerDutyAction action = pagerDutyAction(event).build();
|
||||
XContentBuilder jsonBuilder = jsonBuilder();
|
||||
action.toXContent(jsonBuilder, ToXContent.EMPTY_PARAMS);
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(jsonBuilder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
PagerDutyAction parsedAction = PagerDutyAction.parse("_w1", "_a1", parser);
|
||||
assertThat(parsedAction, notNullValue());
|
||||
assertThat(parsedAction, is(action));
|
||||
}
|
||||
|
||||
public void testParserInvalid() throws Exception {
|
||||
try {
|
||||
XContentBuilder builder = jsonBuilder().startObject().field("unknown_field", "value");
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
PagerDutyAction.parse("_watch", "_action", parser);
|
||||
fail("Expected ElasticsearchParseException but did not happen");
|
||||
} catch (ElasticsearchParseException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IncidentEventDefaultsTests extends ESTestCase {
|
||||
|
||||
public void testConstructor() throws Exception {
|
||||
Settings settings = randomSettings();
|
||||
IncidentEventDefaults defaults = new IncidentEventDefaults(settings);
|
||||
assertThat(defaults.incidentKey, is(settings.get("incident_key", null)));
|
||||
assertThat(defaults.description, is(settings.get("description", null)));
|
||||
assertThat(defaults.clientUrl, is(settings.get("client_url", null)));
|
||||
assertThat(defaults.client, is(settings.get("client", null)));
|
||||
assertThat(defaults.eventType, is(settings.get("event_type", null)));
|
||||
assertThat(defaults.attachPayload, is(settings.getAsBoolean("attach_payload", false)));
|
||||
if (settings.getAsSettings("link").names().isEmpty()) {
|
||||
IncidentEventDefaults.Context.LinkDefaults linkDefaults = new IncidentEventDefaults.Context.LinkDefaults(Settings.EMPTY);
|
||||
assertThat(defaults.link, is(linkDefaults));
|
||||
} else {
|
||||
assertThat(defaults.link, notNullValue());
|
||||
assertThat(defaults.link.href, is(settings.get("link.href", null)));
|
||||
assertThat(defaults.link.text, is(settings.get("link.text", null)));
|
||||
}
|
||||
if (settings.getAsSettings("image").names().isEmpty()) {
|
||||
IncidentEventDefaults.Context.ImageDefaults imageDefaults = new IncidentEventDefaults.Context.ImageDefaults(Settings.EMPTY);
|
||||
assertThat(defaults.image, is(imageDefaults));
|
||||
} else {
|
||||
assertThat(defaults.image, notNullValue());
|
||||
assertThat(defaults.image.href, is(settings.get("image.href", null)));
|
||||
assertThat(defaults.image.alt, is(settings.get("image.alt", null)));
|
||||
assertThat(defaults.image.src, is(settings.get("image.src", null)));
|
||||
}
|
||||
}
|
||||
|
||||
public static Settings randomSettings() {
|
||||
Settings.Builder settings = Settings.builder();
|
||||
if (randomBoolean()) {
|
||||
settings.put("from", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
String[] to = new String[randomIntBetween(1, 3)];
|
||||
for (int i = 0; i < to.length; i++) {
|
||||
to[i] = randomAsciiOfLength(10);
|
||||
}
|
||||
settings.putArray("to", to);
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("text", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("event_type", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("icon", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.fallback", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.color", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.pretext", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.author_name", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.author_link", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.author_icon", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.title", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.title_link", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.text", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.image_url", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.thumb_url", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.field.title", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.field.value", randomAsciiOfLength(10));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
settings.put("attachment.field.short", randomBoolean());
|
||||
}
|
||||
return settings.build();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.settings.SettingsException;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.watcher.actions.slack.service.message.SlackMessageDefaultsTests;
|
||||
import org.elasticsearch.watcher.support.http.HttpClient;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.isOneOf;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PagerDutyAccountsTests extends ESTestCase {
|
||||
|
||||
private HttpClient httpClient;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
httpClient = mock(HttpClient.class);
|
||||
}
|
||||
|
||||
public void testSingleAccount() throws Exception {
|
||||
Settings.Builder builder = Settings.builder()
|
||||
.put("default_account", "account1");
|
||||
addAccountSettings("account1", builder);
|
||||
|
||||
PagerDutyAccounts accounts = new PagerDutyAccounts(builder.build(), httpClient, logger);
|
||||
PagerDutyAccount account = accounts.account("account1");
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account1"));
|
||||
account = accounts.account(null); // falling back on the default
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account1"));
|
||||
}
|
||||
|
||||
public void testSingleAccountNoExplicitDefault() throws Exception {
|
||||
Settings.Builder builder = Settings.builder();
|
||||
addAccountSettings("account1", builder);
|
||||
|
||||
PagerDutyAccounts accounts = new PagerDutyAccounts(builder.build(), httpClient, logger);
|
||||
PagerDutyAccount account = accounts.account("account1");
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account1"));
|
||||
account = accounts.account(null); // falling back on the default
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account1"));
|
||||
}
|
||||
|
||||
public void testMultipleAccounts() throws Exception {
|
||||
Settings.Builder builder = Settings.builder()
|
||||
.put("default_account", "account1");
|
||||
addAccountSettings("account1", builder);
|
||||
addAccountSettings("account2", builder);
|
||||
|
||||
PagerDutyAccounts accounts = new PagerDutyAccounts(builder.build(), httpClient, logger);
|
||||
PagerDutyAccount account = accounts.account("account1");
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account1"));
|
||||
account = accounts.account("account2");
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account2"));
|
||||
account = accounts.account(null); // falling back on the default
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account1"));
|
||||
}
|
||||
|
||||
public void testMultipleAccounts_NoExplicitDefault() throws Exception {
|
||||
Settings.Builder builder = Settings.builder()
|
||||
.put("default_account", "account1");
|
||||
addAccountSettings("account1", builder);
|
||||
addAccountSettings("account2", builder);
|
||||
|
||||
PagerDutyAccounts accounts = new PagerDutyAccounts(builder.build(), httpClient, logger);
|
||||
PagerDutyAccount account = accounts.account("account1");
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account1"));
|
||||
account = accounts.account("account2");
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, equalTo("account2"));
|
||||
account = accounts.account(null);
|
||||
assertThat(account, notNullValue());
|
||||
assertThat(account.name, isOneOf("account1", "account2"));
|
||||
}
|
||||
|
||||
public void testMultipleAccounts_UnknownDefault() throws Exception {
|
||||
try {
|
||||
Settings.Builder builder = Settings.builder()
|
||||
.put("default_account", "unknown");
|
||||
addAccountSettings("account1", builder);
|
||||
addAccountSettings("account2", builder);
|
||||
new PagerDutyAccounts(builder.build(), httpClient, logger);
|
||||
fail("Expected a SettingsException to happen");
|
||||
} catch (SettingsException e) {}
|
||||
}
|
||||
|
||||
public void testNoAccount() throws Exception {
|
||||
try {
|
||||
Settings.Builder builder = Settings.builder();
|
||||
PagerDutyAccounts accounts = new PagerDutyAccounts(builder.build(), httpClient, logger);
|
||||
accounts.account(null);
|
||||
fail("no accounts are configured so trying to get the default account should throw an IllegalStateException");
|
||||
} catch (IllegalStateException e) {}
|
||||
}
|
||||
|
||||
public void testNoAccount_WithDefaultAccount() throws Exception {
|
||||
try {
|
||||
Settings.Builder builder = Settings.builder()
|
||||
.put("default_account", "unknown");
|
||||
new PagerDutyAccounts(builder.build(), httpClient, logger);
|
||||
fail("Expected a SettingsException to happen");
|
||||
} catch (SettingsException e) {}
|
||||
}
|
||||
|
||||
private void addAccountSettings(String name, Settings.Builder builder) {
|
||||
builder.put("account." + name + ".service_api_key", randomAsciiOfLength(50));
|
||||
Settings defaults = SlackMessageDefaultsTests.randomSettings();
|
||||
for (Map.Entry<String, String> setting : defaults.getAsMap().entrySet()) {
|
||||
builder.put("message_defaults." + setting.getKey(), setting.getValue());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.watcher.actions.pagerduty.service;
|
||||
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.elasticsearch.watcher.actions.pagerduty.PagerDutyAction;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
|
||||
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
|
||||
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
import static org.elasticsearch.watcher.actions.ActionBuilders.pagerDutyAction;
|
||||
import static org.elasticsearch.watcher.client.WatchSourceBuilders.watchBuilder;
|
||||
import static org.elasticsearch.watcher.condition.ConditionBuilders.alwaysCondition;
|
||||
import static org.elasticsearch.watcher.input.InputBuilders.simpleInput;
|
||||
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
|
||||
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Network
|
||||
public class PagerDutyServiceTests extends AbstractWatcherIntegrationTestCase {
|
||||
|
||||
@Override
|
||||
protected boolean timeWarped() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean enableShield() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
return Settings.builder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put("watcher.actions.pagerduty.service.account.test_account.service_api_key", "fc082467005d4072a914e0bb041882d0")
|
||||
.build();
|
||||
}
|
||||
|
||||
public void testSendTriggerEvent() throws Exception {
|
||||
PagerDutyService service = getInstanceFromMaster(PagerDutyService.class);
|
||||
|
||||
IncidentEvent event = new IncidentEvent("#testIncidentEvent()", null, null, "PagerDutyServiceTests", "_client_url", "_account", true, new IncidentEventContext[] {
|
||||
IncidentEventContext.link("_href", "_text"),
|
||||
IncidentEventContext.image("_src", "_href", "_alt")
|
||||
});
|
||||
|
||||
Payload payload = new Payload.Simple("_key", "_val");
|
||||
|
||||
PagerDutyAccount account = service.getAccount("test_account");
|
||||
assertThat(account, notNullValue());
|
||||
SentEvent sentEvent = account.send(event, payload);
|
||||
assertThat(sentEvent, notNullValue());
|
||||
assertThat(sentEvent.successful(), is(true));
|
||||
assertThat(sentEvent.request, notNullValue());
|
||||
assertThat(sentEvent.response, notNullValue());
|
||||
assertThat(sentEvent.response.status(), lessThan(300));
|
||||
}
|
||||
|
||||
public void testWatchWithPagerDutyAction() throws Exception {
|
||||
String account = "test_account";
|
||||
PagerDutyAction.Builder actionBuilder = pagerDutyAction(IncidentEvent
|
||||
.templateBuilder("pager duty integration test `{{ctx.payload.ref}}`").setAccount(account));
|
||||
|
||||
PutWatchResponse putWatchResponse = watcherClient().preparePutWatch("1").setSource(watchBuilder()
|
||||
.trigger(schedule(interval("10m")))
|
||||
.input(simpleInput("ref", "testWatchWithPagerDutyAction()"))
|
||||
.condition(alwaysCondition())
|
||||
.addAction("pd", actionBuilder))
|
||||
.execute().get();
|
||||
|
||||
assertThat(putWatchResponse.isCreated(), is(true));
|
||||
|
||||
timeWarp().scheduler().trigger("1");
|
||||
flush();
|
||||
refresh();
|
||||
|
||||
assertWatchWithMinimumPerformedActionsCount("1", 1L, false);
|
||||
SearchResponse response = searchHistory(searchSource().query(boolQuery()
|
||||
.must(termQuery("result.actions.id", "pd"))
|
||||
.must(termQuery("result.actions.type", "pagerduty"))
|
||||
.must(termQuery("result.actions.status", "success"))
|
||||
.must(termQuery("result.actions.pagerduty.sent_event.event.account", account))));
|
||||
|
||||
assertThat(response, notNullValue());
|
||||
assertHitCount(response, 1L);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue