From f896d6689ff0bc0136441b0171ff6380c7918220 Mon Sep 17 00:00:00 2001 From: Damian Mazurkiewicz Date: Sat, 22 Oct 2016 13:14:29 +0100 Subject: [PATCH 1/3] Add Amazon Web Services (AWS) SNS Support --- README.md | 8 +- pom.xml | 12 +-- seyren-core/pom.xml | 5 ++ .../seyren/core/domain/SubscriptionType.java | 19 ++++- .../notification/SnsNotificationService.java | 73 +++++++++++++++++++ .../java/com/seyren/mongo/MongoStore.java | 67 +++++++++-------- .../html/modal-partial-subscription.html | 1 + 7 files changed, 140 insertions(+), 45 deletions(-) create mode 100644 seyren-core/src/main/java/com/seyren/core/service/notification/SnsNotificationService.java diff --git a/README.md b/README.md index b254582f..be50132b 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ Seyren ([/ˈsaɪ.rʌn/](http://en.wikipedia.org/wiki/Wikipedia:IPA_for_English#K [SLF4J](http://www.slf4j.org), [Slack](https://www.slack.com), [SNMP](http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol), -[Twilio](https://www.twilio.com/) - +[Twilio](https://www.twilio.com/), +[SNS](https://aws.amazon.com/sns/) #[](http://i.imgur.com/ahu3aM6.png) @@ -142,6 +142,10 @@ The target for a Slack subscription will be the channel name (including the `#`, ##### [Victorops](https://www.victorops.com/) * `VICTOROPS_REST_ENDPOINT` - The REST Endpoint given by Victorops. See [REST Integration]( http://victorops.force.com/knowledgebase/articles/Integration/Alert-Ingestion-API-Documentation/) Default `` +##### [SNS](https://aws.amazon.com/sns/) +* AWS SNS Client uses [default AWS credentials provider chain.](http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html) +* Target is an ARN of the SNS Topic. + ##### [Proxy](http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/HttpClientBuilder.html#useSystemProperties()) The Proxy settings are changed by using JAVA_OPTS. It important that you exclude your graphite server or you will proxy these requests also. * `JAVA_OPTS Example` JAVA_OPTS="-server -Dhttps.proxyHost=`PROXYSERVER` -Dhttps.proxyPort=`PROXYPORT` -Dhttp.proxyHost=`PROXYSERVER` -Dhttp.proxyPort=`PROXYPORT` -Dhttp.nonProxyHosts=`GRAPHITESERVER|*YOURDOMAIN.COM|INTERNALHIPCHAT`" diff --git a/pom.xml b/pom.xml index a41a4b13..532d55ca 100755 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 3.2.6 - + UTF-8 UTF-8 1.1.2 @@ -59,7 +59,7 @@ 2.5.0 1.1.42 18.0 - 2.0.7 + 2.4.2 1.0.1 2.4 2.6 @@ -68,7 +68,7 @@ 1.4.7 2.6 4.12 - 4.3.6 + 4.5.2 1.7 9.2.2.v20140723 1.3 @@ -77,10 +77,10 @@ 3.2.2 2.5.3 1.7.9 - 2.3.3 + 2.5.2 4.1.4.RELEASE - + 3.0 @@ -189,7 +189,7 @@ - + org.apache.maven.plugins maven-release-plugin diff --git a/seyren-core/pom.xml b/seyren-core/pom.xml index c986ad24..6376e288 100755 --- a/seyren-core/pom.xml +++ b/seyren-core/pom.xml @@ -11,6 +11,11 @@ + + com.amazonaws + aws-java-sdk-sns + 1.11.46 + com.squareup.pagerduty pagerduty-incidents diff --git a/seyren-core/src/main/java/com/seyren/core/domain/SubscriptionType.java b/seyren-core/src/main/java/com/seyren/core/domain/SubscriptionType.java index b7eed588..12fdff84 100644 --- a/seyren-core/src/main/java/com/seyren/core/domain/SubscriptionType.java +++ b/seyren-core/src/main/java/com/seyren/core/domain/SubscriptionType.java @@ -14,7 +14,20 @@ package com.seyren.core.domain; public enum SubscriptionType { - - EMAIL, PAGERDUTY, HIPCHAT, HUBOT, FLOWDOCK, HTTP, IRCCAT, PUSHOVER, LOGGER, SNMP, SLACK, TWILIO, VICTOROPS, - OPSGENIE, BIGPANDA + EMAIL, + PAGERDUTY, + HIPCHAT, + HUBOT, + FLOWDOCK, + HTTP, + IRCCAT, + PUSHOVER, + LOGGER, + SNMP, + SLACK, + TWILIO, + VICTOROPS, + OPSGENIE, + BIGPANDA, + SNS } diff --git a/seyren-core/src/main/java/com/seyren/core/service/notification/SnsNotificationService.java b/seyren-core/src/main/java/com/seyren/core/service/notification/SnsNotificationService.java new file mode 100644 index 00000000..34226740 --- /dev/null +++ b/seyren-core/src/main/java/com/seyren/core/service/notification/SnsNotificationService.java @@ -0,0 +1,73 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.seyren.core.service.notification; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.springframework.mail.javamail.JavaMailSender; + +import com.seyren.core.domain.Alert; +import com.seyren.core.domain.Check; +import com.seyren.core.domain.Subscription; +import com.seyren.core.domain.SubscriptionType; +import com.seyren.core.exception.NotificationFailedException; +import com.seyren.core.util.config.SeyrenConfig; + +import com.amazonaws.services.sns.AmazonSNSClient; +import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.sns.model.PublishRequest; +import com.amazonaws.services.sns.model.PublishResult; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Named +public class SnsNotificationService implements NotificationService { + private static final Logger LOGGER = LoggerFactory.getLogger(SnsNotificationService.class); + + private AmazonSNSClient snsClient; + private SeyrenConfig seyrenConfig; + + @Inject + public SnsNotificationService(SeyrenConfig seyrenConfig) { + this.snsClient = new AmazonSNSClient(); + this.seyrenConfig = seyrenConfig; + } + + @Override + public void sendNotification(Check check, Subscription subscription, List alerts) { + String topicArn = subscription.getTarget(); + + String msg = String.format("Seyren notification '%s' changed state to '%s'", + check.getName(), + check.getState().name()); + + LOGGER.info("Sending Notification to SNS Topic: " + topicArn + " with message " + msg); + + PublishRequest publishRequest = new PublishRequest(topicArn, msg); + PublishResult publishResult = snsClient.publish(publishRequest); + + LOGGER.info("Send Notification - " + publishResult.getMessageId()); + } + + @Override + public boolean canHandle(SubscriptionType subscriptionType) { + return subscriptionType == SubscriptionType.SNS; + } +} diff --git a/seyren-mongo/src/main/java/com/seyren/mongo/MongoStore.java b/seyren-mongo/src/main/java/com/seyren/mongo/MongoStore.java index 34adbdae..fe13caa9 100644 --- a/seyren-mongo/src/main/java/com/seyren/mongo/MongoStore.java +++ b/seyren-mongo/src/main/java/com/seyren/mongo/MongoStore.java @@ -55,12 +55,12 @@ @Named public class MongoStore implements ChecksStore, AlertsStore, SubscriptionsStore { - + private static final Logger LOGGER = LoggerFactory.getLogger(MongoStore.class); - + private MongoMapper mapper = new MongoMapper(); private DB mongo; - + @Inject public MongoStore(SeyrenConfig seyrenConfig) { try { @@ -75,7 +75,7 @@ public MongoStore(SeyrenConfig seyrenConfig) { throw new RuntimeException(e); } } - + private void bootstrapMongo() { LOGGER.info("Bootstrapping Mongo indexes. Depending on the number of checks and alerts you've got it may take a little while."); try { @@ -125,11 +125,11 @@ private void addTargetHashToAlerts() { getAlertsCollection().save(mapper.alertToDBObject(alert)); } } - + private DBCollection getChecksCollection() { return mongo.getCollection("checks"); } - + private DBCollection getAlertsCollection() { return mongo.getCollection("alerts"); } @@ -146,7 +146,7 @@ protected SeyrenResponse executeQueryAndCollectResponse(DBObject query) { .withValues(checks) .withTotal(dbc.count()); } - + @Override public SeyrenResponse getChecks(Boolean enabled, Boolean live) { List checks = new ArrayList(); @@ -165,23 +165,23 @@ public SeyrenResponse getChecks(Boolean enabled, Boolean live) { .withValues(checks) .withTotal(dbc.count()); } - + @Override public SeyrenResponse getChecksByState(Set states, Boolean enabled) { List checks = new ArrayList(); - + DBObject query = new BasicDBObject(); query.put("state", object("$in", states.toArray())); if (enabled != null) { query.put("enabled", enabled); } DBCursor dbc = getChecksCollection().find(query); - + while (dbc.hasNext()) { checks.add(mapper.checkFrom(dbc.next())); } dbc.close(); - + return new SeyrenResponse() .withValues(checks) .withTotal(dbc.count()); @@ -215,26 +215,26 @@ public Check getCheck(String checkId) { } return mapper.checkFrom(dbo); } - + @Override public void deleteCheck(String checkId) { getChecksCollection().remove(forId(checkId)); deleteAlerts(checkId, null); } - + @Override public Check createCheck(Check check) { check.setId(ObjectId.get().toString()); getChecksCollection().insert(mapper.checkToDBObject(check)); return check; } - + @Override public Check saveCheck(Check check) { DBObject findObject = forId(check.getId()); - + DateTime lastCheck = check.getLastCheck(); - + DBObject partialObject = object("name", check.getName()) .with("description", check.getDescription()) .with("target", check.getTarget()) @@ -247,28 +247,28 @@ public Check saveCheck(Check check) { .with("allowNoData", check.isAllowNoData()) .with("lastCheck", lastCheck == null ? null : new Date(lastCheck.getMillis())) .with("state", check.getState().toString()); - + DBObject setObject = object("$set", partialObject); - + getChecksCollection().update(findObject, setObject); - + return check; } - + @Override public Check updateStateAndLastCheck(String checkId, AlertType state, DateTime lastCheck) { DBObject findObject = forId(checkId); - + DBObject partialObject = object("lastCheck", new Date(lastCheck.getMillis())) .with("state", state.toString()); - + DBObject setObject = object("$set", partialObject); - + getChecksCollection().update(findObject, setObject); return getCheck(checkId); } - + @Override public Alert createAlert(String checkId, Alert alert) { alert.setId(ObjectId.get().toString()); @@ -276,7 +276,7 @@ public Alert createAlert(String checkId, Alert alert) { getAlertsCollection().insert(mapper.alertToDBObject(alert)); return alert; } - + @Override public SeyrenResponse getAlerts(String checkId, int start, int items) { DBCursor dbc = getAlertsCollection().find(object("checkId", checkId)).sort(object("timestamp", -1)).skip(start).limit(items); @@ -291,7 +291,7 @@ public SeyrenResponse getAlerts(String checkId, int start, int items) { .withStart(start) .withTotal(dbc.count()); } - + @Override public SeyrenResponse getAlerts(int start, int items) { DBCursor dbc = getAlertsCollection().find().sort(object("timestamp", -1)).skip(start).limit(items); @@ -306,18 +306,18 @@ public SeyrenResponse getAlerts(int start, int items) { .withStart(start) .withTotal(dbc.count()); } - + @Override public void deleteAlerts(String checkId, DateTime before) { DBObject query = object("checkId", checkId); - + if (before != null) { query.put("timestamp", object("$lt", new Date(before.getMillis()))); } - + getAlertsCollection().remove(query); } - + @Override public Alert getLastAlertForTargetOfCheck(String target, String checkId) { DBObject query = object("checkId", checkId).with("targetHash", TargetHash.create(target)); @@ -331,7 +331,7 @@ public Alert getLastAlertForTargetOfCheck(String target, String checkId) { } return null; } - + @Override public Subscription createSubscription(String checkId, Subscription subscription) { subscription.setId(ObjectId.get().toString()); @@ -340,14 +340,14 @@ public Subscription createSubscription(String checkId, Subscription subscription getChecksCollection().update(check, query); return subscription; } - + @Override public void deleteSubscription(String checkId, String subscriptionId) { DBObject check = forId(checkId); BasicDBObject subscription = object("$pull", object("subscriptions", forId(subscriptionId))); getChecksCollection().update(check, subscription); } - + @Override public void updateSubscription(String checkId, Subscription subscription) { DBObject subscriptionObject = mapper.subscriptionToDBObject(subscription); @@ -356,5 +356,4 @@ public void updateSubscription(String checkId, Subscription subscription) { DBObject updateObject = object("$set", object("subscriptions.$", subscriptionObject)); getChecksCollection().update(checkFindObject, updateObject); } - } diff --git a/seyren-web/src/main/webapp/html/modal-partial-subscription.html b/seyren-web/src/main/webapp/html/modal-partial-subscription.html index ac5e119c..bf2281d2 100644 --- a/seyren-web/src/main/webapp/html/modal-partial-subscription.html +++ b/seyren-web/src/main/webapp/html/modal-partial-subscription.html @@ -31,6 +31,7 @@