Add registry email to bcc for outgoing DNS failure emails (#1755)

This commit is contained in:
Pavlo Tkach 2022-08-25 14:15:20 -04:00 committed by GitHub
parent dbc6cd2377
commit f65dba8ec5
5 changed files with 72 additions and 27 deletions

View file

@ -1320,8 +1320,14 @@ public final class RegistryConfig {
@Provides @Provides
@Config("registrySupportEmail") @Config("registrySupportEmail")
public static String provideRegistrySupportEmail(RegistryConfigSettings config) { public static InternetAddress provideRegistrySupportEmail(RegistryConfigSettings config) {
return config.dnsUpdate.registrySupportEmail; return parseEmailAddress(config.dnsUpdate.registrySupportEmail);
}
@Provides
@Config("registryCcEmail")
public static InternetAddress provideRegistryCcEmail(RegistryConfigSettings config) {
return parseEmailAddress(config.dnsUpdate.registryCcEmail);
} }
@Provides @Provides

View file

@ -254,5 +254,6 @@ public class RegistryConfigSettings {
public String dnsUpdateFailEmailBodyText; public String dnsUpdateFailEmailBodyText;
public String dnsUpdateFailRegistryName; public String dnsUpdateFailRegistryName;
public String registrySupportEmail; public String registrySupportEmail;
public String registryCcEmail;
} }
} }

View file

@ -481,6 +481,7 @@ contactHistory:
dnsUpdate: dnsUpdate:
dnsUpdateFailRegistryName: Example name dnsUpdateFailRegistryName: Example name
registrySupportEmail: email@example.com registrySupportEmail: email@example.com
registryCcEmail: email@example.com
# Email subject text template to notify partners after repeatedly failing DNS update # Email subject text template to notify partners after repeatedly failing DNS update
dnsUpdateFailEmailSubjectText: "[ACTION REQUIRED]: Incomplete DNS Update" dnsUpdateFailEmailSubjectText: "[ACTION REQUIRED]: Incomplete DNS Update"
# Email body text template for failing DNS update that accepts 5 parameters: # Email body text template for failing DNS update that accepts 5 parameters:

View file

@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.flogger.FluentLogger; import com.google.common.flogger.FluentLogger;
import com.google.common.net.InternetDomainName; import com.google.common.net.InternetDomainName;
import dagger.Lazy;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.dns.DnsMetrics.ActionStatus; import google.registry.dns.DnsMetrics.ActionStatus;
import google.registry.dns.DnsMetrics.CommitStatus; import google.registry.dns.DnsMetrics.CommitStatus;
@ -52,14 +53,16 @@ import google.registry.request.Parameter;
import google.registry.request.Response; import google.registry.request.Response;
import google.registry.request.auth.Auth; import google.registry.request.auth.Auth;
import google.registry.request.lock.LockHandler; import google.registry.request.lock.LockHandler;
import google.registry.ui.server.SendEmailUtils;
import google.registry.util.Clock; import google.registry.util.Clock;
import google.registry.util.CloudTasksUtils; import google.registry.util.CloudTasksUtils;
import google.registry.util.DomainNameUtils; import google.registry.util.DomainNameUtils;
import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.mail.internet.InternetAddress;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Duration; import org.joda.time.Duration;
@ -109,11 +112,13 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
private final Clock clock; private final Clock clock;
private final CloudTasksUtils cloudTasksUtils; private final CloudTasksUtils cloudTasksUtils;
private final Response response; private final Response response;
private final SendEmailUtils sendEmailUtils; private final SendEmailService sendEmailService;
private final String dnsUpdateFailEmailSubjectText; private final String dnsUpdateFailEmailSubjectText;
private final String dnsUpdateFailEmailBodyText; private final String dnsUpdateFailEmailBodyText;
private final String dnsUpdateFailRegistryName; private final String dnsUpdateFailRegistryName;
private final String registrySupportEmail; private final Lazy<InternetAddress> registrySupportEmail;
private final Lazy<InternetAddress> registryCcEmail;
private final InternetAddress gSuiteOutgoingEmailAddress;
@Inject @Inject
public PublishDnsUpdatesAction( public PublishDnsUpdatesAction(
@ -129,7 +134,9 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
@Config("dnsUpdateFailEmailSubjectText") String dnsUpdateFailEmailSubjectText, @Config("dnsUpdateFailEmailSubjectText") String dnsUpdateFailEmailSubjectText,
@Config("dnsUpdateFailEmailBodyText") String dnsUpdateFailEmailBodyText, @Config("dnsUpdateFailEmailBodyText") String dnsUpdateFailEmailBodyText,
@Config("dnsUpdateFailRegistryName") String dnsUpdateFailRegistryName, @Config("dnsUpdateFailRegistryName") String dnsUpdateFailRegistryName,
@Config("registrySupportEmail") String registrySupportEmail, @Config("registrySupportEmail") Lazy<InternetAddress> registrySupportEmail,
@Config("registryCcEmail") Lazy<InternetAddress> registryCcEmail,
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
@Header(APP_ENGINE_RETRY_HEADER) Optional<Integer> appEngineRetryCount, @Header(APP_ENGINE_RETRY_HEADER) Optional<Integer> appEngineRetryCount,
@Header(CLOUD_TASKS_RETRY_HEADER) Optional<Integer> cloudTasksRetryCount, @Header(CLOUD_TASKS_RETRY_HEADER) Optional<Integer> cloudTasksRetryCount,
DnsQueue dnsQueue, DnsQueue dnsQueue,
@ -138,13 +145,13 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
LockHandler lockHandler, LockHandler lockHandler,
Clock clock, Clock clock,
CloudTasksUtils cloudTasksUtils, CloudTasksUtils cloudTasksUtils,
SendEmailUtils sendEmailUtils, SendEmailService sendEmailService,
Response response) { Response response) {
this.dnsQueue = dnsQueue; this.dnsQueue = dnsQueue;
this.dnsWriterProxy = dnsWriterProxy; this.dnsWriterProxy = dnsWriterProxy;
this.dnsMetrics = dnsMetrics; this.dnsMetrics = dnsMetrics;
this.timeout = timeout; this.timeout = timeout;
this.sendEmailUtils = sendEmailUtils; this.sendEmailService = sendEmailService;
this.retryCount = this.retryCount =
cloudTasksRetryCount.orElse( cloudTasksRetryCount.orElse(
appEngineRetryCount.orElseThrow( appEngineRetryCount.orElseThrow(
@ -165,6 +172,8 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
this.dnsUpdateFailEmailSubjectText = dnsUpdateFailEmailSubjectText; this.dnsUpdateFailEmailSubjectText = dnsUpdateFailEmailSubjectText;
this.dnsUpdateFailRegistryName = dnsUpdateFailRegistryName; this.dnsUpdateFailRegistryName = dnsUpdateFailRegistryName;
this.registrySupportEmail = registrySupportEmail; this.registrySupportEmail = registrySupportEmail;
this.registryCcEmail = registryCcEmail;
this.gSuiteOutgoingEmailAddress = gSuiteOutgoingEmailAddress;
} }
private void recordActionResult(ActionStatus status) { private void recordActionResult(ActionStatus status) {
@ -267,25 +276,48 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
return null; return null;
} }
private InternetAddress emailToInternetAddress(String email) {
try {
return new InternetAddress(email, true);
} catch (Exception e) {
logger.atWarning().withCause(e).log(
String.format(
"Could not parse email contact %s to send DNS failure notification", email));
return null;
}
}
/** Sends an email to partners regarding a failure during DNS update */ /** Sends an email to partners regarding a failure during DNS update */
private void notifyWithEmailAboutDnsUpdateFailure( private void notifyWithEmailAboutDnsUpdateFailure(
String registrarId, String hostOrDomainName, Boolean isHost) { String registrarId, String hostOrDomainName, Boolean isHost) {
Optional<Registrar> registrar = Registrar.loadByRegistrarIdCached(registrarId); Optional<Registrar> registrar = Registrar.loadByRegistrarIdCached(registrarId);
if (registrar.isPresent()) { if (registrar.isPresent()) {
sendEmailUtils.sendEmail( String body =
dnsUpdateFailEmailSubjectText,
String.format( String.format(
dnsUpdateFailEmailBodyText, dnsUpdateFailEmailBodyText,
registrar.get().getRegistrarName(), registrar.get().getRegistrarName(),
hostOrDomainName, hostOrDomainName,
isHost ? "host" : "domain", isHost ? "host" : "domain",
registrySupportEmail, registrySupportEmail.get().getAddress(),
dnsUpdateFailRegistryName), dnsUpdateFailRegistryName);
ImmutableList<InternetAddress> recipients =
registrar.get().getContacts().stream() registrar.get().getContacts().stream()
.filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN)) .filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN))
.map(RegistrarPoc::getEmailAddress) .map(RegistrarPoc::getEmailAddress)
.collect(toImmutableList())); .map(this::emailToInternetAddress)
.collect(toImmutableList());
sendEmailService.sendEmail(
EmailMessage.newBuilder()
.setBody(body)
.setSubject(dnsUpdateFailEmailSubjectText)
.setRecipients(recipients)
.addBcc(registryCcEmail.get())
.setFrom(gSuiteOutgoingEmailAddress)
.build());
} else { } else {
logger.atSevere().log(String.format("Could not find registrar %s", registrarId)); logger.atSevere().log(String.format("Could not find registrar %s", registrarId));
} }

View file

@ -38,9 +38,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import dagger.Lazy;
import google.registry.dns.DnsMetrics.ActionStatus; import google.registry.dns.DnsMetrics.ActionStatus;
import google.registry.dns.DnsMetrics.CommitStatus; import google.registry.dns.DnsMetrics.CommitStatus;
import google.registry.dns.DnsMetrics.PublishStatus; import google.registry.dns.DnsMetrics.PublishStatus;
@ -55,7 +55,7 @@ import google.registry.testing.CloudTasksHelper.TaskMatcher;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.FakeLockHandler; import google.registry.testing.FakeLockHandler;
import google.registry.testing.FakeResponse; import google.registry.testing.FakeResponse;
import google.registry.ui.server.SendEmailUtils; import google.registry.testing.Lazies;
import google.registry.util.EmailMessage; import google.registry.util.EmailMessage;
import google.registry.util.SendEmailService; import google.registry.util.SendEmailService;
import java.util.Optional; import java.util.Optional;
@ -83,19 +83,17 @@ public class PublishDnsUpdatesActionTest {
private final DnsQueue dnsQueue = mock(DnsQueue.class); private final DnsQueue dnsQueue = mock(DnsQueue.class);
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(); private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
private PublishDnsUpdatesAction action; private PublishDnsUpdatesAction action;
private InternetAddress outgoingRegistry;
private Lazy<InternetAddress> registrySupportEmail;
private Lazy<InternetAddress> registryCcEmail;
private final SendEmailService emailService = mock(SendEmailService.class); private final SendEmailService emailService = mock(SendEmailService.class);
private SendEmailUtils sendEmailUtils;
@BeforeEach @BeforeEach
void beforeEach() throws Exception { void beforeEach() throws Exception {
sendEmailUtils =
new SendEmailUtils(
new InternetAddress("outgoing@registry.example"),
"UnitTest Registry",
ImmutableList.of("notification@test.example", "notification2@test.example"),
emailService);
createTld("xn--q9jyb4c"); createTld("xn--q9jyb4c");
outgoingRegistry = new InternetAddress("outgoing@registry.example");
registrySupportEmail = Lazies.of(new InternetAddress("registry@test.com"));
registryCcEmail = Lazies.of(new InternetAddress("registry-cc@test.com"));
persistResource( persistResource(
Registry.get("xn--q9jyb4c") Registry.get("xn--q9jyb4c")
.asBuilder() .asBuilder()
@ -143,6 +141,7 @@ public class PublishDnsUpdatesActionTest {
int lockIndex, int lockIndex,
int numPublishLocks, int numPublishLocks,
LockHandler lockHandler) { LockHandler lockHandler) {
return new PublishDnsUpdatesAction( return new PublishDnsUpdatesAction(
dnsWriterString, dnsWriterString,
clock.nowUtc().minusHours(1), clock.nowUtc().minusHours(1),
@ -155,8 +154,10 @@ public class PublishDnsUpdatesActionTest {
Duration.standardSeconds(10), Duration.standardSeconds(10),
"Subj", "Subj",
"Body %1$s %2$s %3$s %4$s %5$s", "Body %1$s %2$s %3$s %4$s %5$s",
"registry@test.com",
"awesomeRegistry", "awesomeRegistry",
registrySupportEmail,
registryCcEmail,
outgoingRegistry,
Optional.ofNullable(retryCount), Optional.ofNullable(retryCount),
Optional.empty(), Optional.empty(),
dnsQueue, dnsQueue,
@ -165,7 +166,7 @@ public class PublishDnsUpdatesActionTest {
lockHandler, lockHandler,
clock, clock,
cloudTasksHelper.getTestCloudTasksUtils(), cloudTasksHelper.getTestCloudTasksUtils(),
sendEmailUtils, emailService,
response); response);
} }
@ -413,7 +414,9 @@ public class PublishDnsUpdatesActionTest {
assertThat(emailMessage.subject()).isEqualTo("Subj"); assertThat(emailMessage.subject()).isEqualTo("Subj");
assertThat(emailMessage.body()) assertThat(emailMessage.body())
.isEqualTo( .isEqualTo(
"Body The Registrar example.xn--q9jyb4c domain awesomeRegistry registry@test.com"); "Body The Registrar example.xn--q9jyb4c domain registry@test.com awesomeRegistry");
assertThat(emailMessage.bccs().stream().findFirst().get().toString())
.isEqualTo("registry-cc@test.com");
} }
@Test @Test
@ -429,7 +432,9 @@ public class PublishDnsUpdatesActionTest {
assertThat(emailMessage.subject()).isEqualTo("Subj"); assertThat(emailMessage.subject()).isEqualTo("Subj");
assertThat(emailMessage.body()) assertThat(emailMessage.body())
.isEqualTo( .isEqualTo(
"Body The Registrar ns1.example.xn--q9jyb4c host awesomeRegistry registry@test.com"); "Body The Registrar ns1.example.xn--q9jyb4c host registry@test.com awesomeRegistry");
assertThat(emailMessage.bccs().stream().findFirst().get().toString())
.isEqualTo("registry-cc@test.com");
} }
@Test @Test