Skip to content
Permalink
Browse files
IGNITE-11947: Fix for handling failures of email/slack sending, retri…
…es added
  • Loading branch information
dspavlov committed Jul 1, 2019
1 parent ab47aab commit 8f69c4a4d3c6c84f217a4c87e03af28c97c5c2bd
Showing 9 changed files with 178 additions and 94 deletions.
@@ -23,12 +23,14 @@
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Map;
import java.util.TreeSet;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.ignite.tcbot.persistence.Persisted;
import org.apache.ignite.tcbot.common.util.TimeUtil;
import org.jetbrains.annotations.Nullable;

/**
* Issue used both for saving into DB and in UI (in issue history).
@@ -67,7 +69,13 @@ public class Issue {
/** Set of build tags detected. */
public Set<String> buildTags = new TreeSet<>();

public java.util.Map<String, Integer> stat = new HashMap<>();
/** Statistics of subscribers for this issue. Filled accordignly recent update. */
public Map<String, Object> stat = new HashMap<>();

/** Notification failed: Map from address to exception text */
@Nullable public Map<String, String> notificationFailed = new HashMap<>();

public int notificationRetry = 0;

public Issue(IssueKey issueKey, IssueType type,
@Nullable Long buildStartTs) {
@@ -235,4 +243,32 @@ public String toPlainText(boolean includeChangesInfo) {
public String type() {
return type;
}

/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Issue issue = (Issue)o;
return notificationRetry == issue.notificationRetry &&
Objects.equals(type, issue.type) &&
Objects.equals(displayType, issue.displayType) &&
Objects.equals(trackedBranchName, issue.trackedBranchName) &&
Objects.equals(issueKey, issue.issueKey) &&
Objects.equals(changes, issue.changes) &&
Objects.equals(addressNotified, issue.addressNotified) &&
Objects.equals(webUrl, issue.webUrl) &&
Objects.equals(displayName, issue.displayName) &&
Objects.equals(buildStartTs, issue.buildStartTs) &&
Objects.equals(detectedTs, issue.detectedTs) &&
Objects.equals(buildTags, issue.buildTags) &&
Objects.equals(stat, issue.stat) &&
Objects.equals(notificationFailed, issue.notificationFailed);
}

/** {@inheritDoc} */
@Override public int hashCode() {
return Objects.hash(type, displayType, trackedBranchName, issueKey, changes, addressNotified, webUrl, displayName, buildStartTs, detectedTs, buildTags, stat, notificationFailed, notificationRetry);
}
}
@@ -20,6 +20,7 @@
import java.util.HashMap;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;
import javax.cache.Cache;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -53,17 +54,40 @@ public static IgniteCache<IssueKey, Issue> botDetectedIssuesCache(Ignite ignite)
}

/** {@inheritDoc} */
@Override public boolean setNotified(IssueKey issueKey, String to) {
@Override public boolean getIsNewAndSetNotified(IssueKey issueKey, String to,
@Nullable Exception e) {
Issue issue = cache().get(issueKey);
if (issue == null)
return false;

boolean add = issue.addressNotified.add(to);
boolean update;
if (e == null) {
if (issue.notificationRetry >= 2 && issue.notificationFailed.containsKey(to))
return false; // no more tries;

if (add)
update = issue.addressNotified.add(to);

if (update && issue.notificationFailed != null)
issue.notificationFailed.remove(to);
}
else {
if (issue.notificationRetry < 2)
issue.addressNotified.remove(to);

issue.notificationRetry++;

if (issue.notificationFailed == null)
issue.notificationFailed = new HashMap<>();

issue.notificationFailed.put(to, e.getClass().getSimpleName() + ": " + e.getMessage());

update = true;
}

if (update)
cache().put(issueKey, issue);

return add;
return update;
}

@Override
@@ -73,15 +97,17 @@ public void saveIssueSubscribersStat(IssueKey issueKey, int cntSrvAllowed, int c
if (issue == null)
return;

int hashCode = issue.hashCode();

if (issue.stat == null)
issue.stat = new HashMap<>();

issue.stat.put("cntSrvAllowed", cntSrvAllowed);
issue.stat.put("cntSubscribed", cntSubscribed);
issue.stat.put("cntTagsFilterPassed", cntTagsFilterPassed);

cache().put(issueKey, issue);

if (issue.hashCode() != hashCode)
cache().put(issueKey, issue); // protect from odd writes
}

/** {@inheritDoc} */
@@ -35,8 +35,8 @@
* Class for sending email with configured credentials.
*/
public class EmailSender {
public static boolean sendEmail(String to, String subject, String html, String plainText,
NotificationsConfig notifications) {
public static void sendEmail(String to, String subject, String html, String plainText,
NotificationsConfig notifications) throws MessagingException {

String user = notifications.emailUsernameMandatory();

@@ -58,42 +58,34 @@ public static boolean sendEmail(String to, String subject, String html, String p
}
});

try {
// Create a default MimeMessage object.
MimeMessage msg = new MimeMessage(ses);
// Create a default MimeMessage object.
MimeMessage msg = new MimeMessage(ses);

// Set From: header field of the header.
msg.setFrom(new InternetAddress(from));
// Set From: header field of the header.
msg.setFrom(new InternetAddress(from));

// Set To: header field of the header.
msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
// Set To: header field of the header.
msg.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

// Set Subject: header field
msg.setSubject(subject);
// Set Subject: header field
msg.setSubject(subject);

final MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent(plainText, "text/plain");
// HTML version
final MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(html, "text/html");
final MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent(plainText, "text/plain");
// HTML version
final MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(html, "text/html");

// Create the Multipart. Add BodyParts to it.
final Multipart mp = new MimeMultipart("alternative");
mp.addBodyPart(textPart);
mp.addBodyPart(htmlPart);
// Set Multipart as the message's content
msg.setContent(mp);
// Create the Multipart. Add BodyParts to it.
final Multipart mp = new MimeMultipart("alternative");
mp.addBodyPart(textPart);
mp.addBodyPart(htmlPart);
// Set Multipart as the message's content
msg.setContent(mp);

// Send message
Transport.send(msg);
// Send message
Transport.send(msg);

System.out.println("Sent message successfully to [" + to + "]...");

return true;
}
catch (MessagingException mex) {
mex.printStackTrace();
}
return false;
System.out.println("Sent message successfully to [" + to + "]...");
}
}
@@ -34,7 +34,7 @@
*
*/
public class SlackSender {
public static boolean sendMessage(String addr, String msg,
public static void sendMessage(String addr, String msg,
NotificationsConfig cfg) throws IOException {
String authTok = cfg.slackAuthToken();
Preconditions.checkState(!isNullOrEmpty(authTok), "notifications:\"{}\" property should be filled in branches.json");
@@ -49,11 +49,8 @@ public static boolean sendMessage(String addr, String msg,

SlackChannel slackCh = ses.findChannelByName(ch);

if (slackCh == null) {
System.err.println("Failed to find channel [" + addr + "]: Notification not send [" + msg + "]");

return false;
}
if (slackCh == null)
throw new RuntimeException("Failed to find channel [" + addr + "]: Notification not send [" + msg + "]");

SlackMessageHandle<SlackMessageReply> handle = ses.sendMessage(slackCh, msg);

@@ -62,22 +59,16 @@ public static boolean sendMessage(String addr, String msg,
else {
SlackUser user = ses.findUserByUserName(addr); //make sure bot is a member of the user.

if (user == null) {
System.err.println("Failed to find user [" + addr + "]: Notification not send [" + msg + "]");

return false;
}
if (user == null)
throw new RuntimeException("Failed to find user [" + addr + "]: Notification not send [" + msg + "]");

SlackMessageHandle<SlackMessageReply> handle = ses.sendMessageToUser(user, msg, null);

System.out.println("Message to user " + addr + " " + msg + "; acked: " + handle.isAcked());

}
}
finally {
ses.disconnect();
}

return true;
}
}
@@ -29,6 +29,11 @@
* Utility class for local connection to TC helper DB (server) and any manipulations with data needed.
*/
public class ClientTmpHelper {
public static void main(String[] args) {
//select some main option here.
mainDropIssHist(args);
}

/**
* @param args Args.
*/
@@ -47,7 +52,7 @@ public static void mainConsistCheck(String[] args) {
/**
* @param args Args.
*/
public static void main0(String[] args) {
public static void mainDropIssHist(String[] args) {
Ignite ignite = TcHelperDb.startClient();

IgniteCache<Object, Object> cache = ignite.cache(IssuesStorage.BOT_DETECTED_ISSUES);
@@ -56,7 +61,10 @@ public static void main0(String[] args) {
issue -> {
Object key = issue.getKey();
Issue val = (Issue)issue.getValue();
// val.addressNotified.clear();

if(val.issueKey.testOrBuildName.contains(
"GridCachePartitionEvictionDuringReadThroughSelfTest.testPartitionRent"))
val.addressNotified.clear();

cache.put(key, val);
}
@@ -74,9 +82,6 @@ public static void mainDeletePRs(String[] args) {
ignite.close();
}

public static void main(String[] args) {
mainDeleteTickets(args);
}

public static void mainDeleteTickets(String[] args) {
Ignite ignite = TcHelperDb.startClient();
@@ -18,6 +18,7 @@
package org.apache.ignite.ci.tcbot.issue;

import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.apache.ignite.ci.issue.Issue;
import org.apache.ignite.ci.issue.IssueKey;

@@ -38,9 +39,10 @@ public interface IIssuesStorage {
* Checks and saves address was notified (NotThreadSafe)
* @param key issue key.
* @param addr Address to register as notified.
* @param e Exception. Null means notification was successfull. Non null means notification failed
* @return update successful. This address was not notified before.
*/
public boolean setNotified(IssueKey key, String addr);
public boolean getIsNewAndSetNotified(IssueKey key, String addr, @Nullable Exception e);

public void saveIssueSubscribersStat(IssueKey key, int cntSrvAllowed, int cntSubscribed, int cntTagsFilterPassed);
}

0 comments on commit 8f69c4a

Please sign in to comment.