1
0
mirror of https://github.com/google/nomulus synced 2026-07-06 16:16:38 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
sarahcaseybot 4ede5f0c8a Prevent saving duplicate rows in spec11 pipeline (#1810)
* Prevent saving duplicate rows in spec11 pipeline

* Chain applies together
2022-12-15 15:51:28 -05:00
Lai Jiang 2292bfcaed Remove pipline servlet endpoint mapping (#1885) 2022-12-14 16:28:57 -05:00
sarahcaseybot b056d2945f Add package monitoring for active domain limits (#1867)
* Add monitoring for package active domain limits

* Reformat action class

* Fix a bunch of nits

* Change native query to HQL
2022-12-14 12:10:10 -05:00
9 changed files with 552 additions and 103 deletions
@@ -15,8 +15,10 @@ package google.registry.batch;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.END_OF_TIME;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
import google.registry.config.RegistryConfig.Config;
@@ -28,8 +30,10 @@ import google.registry.request.Action;
import google.registry.request.Action.Service;
import google.registry.request.auth.Auth;
import google.registry.ui.server.SendEmailUtils;
import google.registry.util.Clock;
import java.util.Optional;
import javax.inject.Inject;
import org.joda.time.Days;
/**
* An action that checks all {@link PackagePromotion} objects for compliance with their max create
@@ -44,77 +48,164 @@ public class CheckPackagesComplianceAction implements Runnable {
public static final String PATH = "/_dr/task/checkPackagesCompliance";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final SendEmailUtils sendEmailUtils;
private final String packageCreateLimitEmailSubjectText;
private final String packageCreateLimitEmailBodyText;
private final Clock clock;
private final String packageCreateLimitEmailSubject;
private final String packageDomainLimitWarningEmailSubject;
private final String packageDomainLimitUpgradeEmailSubject;
private final String packageCreateLimitEmailBody;
private final String packageDomainLimitWarningEmailBody;
private final String packageDomainLimitUpgradeEmailBody;
private final String registrySupportEmail;
private static final int THIRTY_DAYS = 30;
private static final int FORTY_DAYS = 40;
@Inject
public CheckPackagesComplianceAction(
SendEmailUtils sendEmailUtils,
@Config("packageCreateLimitEmailSubjectText") String packageCreateLimitEmailSubjectText,
@Config("packageCreateLimitEmailBodyText") String packageCreateLimitEmailBodyText,
Clock clock,
@Config("packageCreateLimitEmailSubject") String packageCreateLimitEmailSubject,
@Config("packageDomainLimitWarningEmailSubject") String packageDomainLimitWarningEmailSubject,
@Config("packageDomainLimitUpgradeEmailSubject") String packageDomainLimitUpgradeEmailSubject,
@Config("packageCreateLimitEmailBody") String packageCreateLimitEmailBody,
@Config("packageDomainLimitWarningEmailBody") String packageDomainLimitWarningEmailBody,
@Config("packageDomainLimitUpgradeEmailBody") String packageDomainLimitUpgradeEmailBody,
@Config("registrySupportEmail") String registrySupportEmail) {
this.sendEmailUtils = sendEmailUtils;
this.packageCreateLimitEmailSubjectText = packageCreateLimitEmailSubjectText;
this.packageCreateLimitEmailBodyText = packageCreateLimitEmailBodyText;
this.clock = clock;
this.packageCreateLimitEmailSubject = packageCreateLimitEmailSubject;
this.packageDomainLimitWarningEmailSubject = packageDomainLimitWarningEmailSubject;
this.packageDomainLimitUpgradeEmailSubject = packageDomainLimitUpgradeEmailSubject;
this.packageCreateLimitEmailBody = packageCreateLimitEmailBody;
this.packageDomainLimitWarningEmailBody = packageDomainLimitWarningEmailBody;
this.packageDomainLimitUpgradeEmailBody = packageDomainLimitUpgradeEmailBody;
this.registrySupportEmail = registrySupportEmail;
}
@Override
public void run() {
tm().transact(
() -> {
ImmutableList<PackagePromotion> packages = tm().loadAllOf(PackagePromotion.class);
ImmutableList.Builder<PackagePromotion> packagesOverCreateLimit =
new ImmutableList.Builder<>();
for (PackagePromotion packagePromo : packages) {
Long creates =
(Long)
tm().query(
"SELECT COUNT(*) FROM DomainHistory WHERE current_package_token ="
+ " :token AND modificationTime >= :lastBilling AND type ="
+ " 'DOMAIN_CREATE'")
.setParameter("token", packagePromo.getToken().getKey().toString())
.setParameter(
"lastBilling", packagePromo.getNextBillingDate().minusYears(1))
.getSingleResult();
if (creates > packagePromo.getMaxCreates()) {
int overage = Ints.saturatedCast(creates) - packagePromo.getMaxCreates();
logger.atInfo().log(
"Package with package token %s has exceeded their max domain creation limit"
+ " by %d name(s).",
packagePromo.getToken().getKey(), overage);
packagesOverCreateLimit.add(packagePromo);
}
}
if (packagesOverCreateLimit.build().isEmpty()) {
logger.atInfo().log("Found no packages over their create limit.");
} else {
logger.atInfo().log(
"Found %d packages over their create limit.",
packagesOverCreateLimit.build().size());
for (PackagePromotion packagePromotion : packagesOverCreateLimit.build()) {
AllocationToken packageToken = tm().loadByKey(packagePromotion.getToken());
Optional<Registrar> registrar =
Registrar.loadByRegistrarIdCached(
packageToken.getAllowedRegistrarIds().iterator().next());
if (registrar.isPresent()) {
String body =
String.format(
packageCreateLimitEmailBodyText,
registrar.get().getRegistrarName(),
packageToken.getToken(),
registrySupportEmail);
sendNotification(
packageToken, packageCreateLimitEmailSubjectText, body, registrar.get());
} else {
logger.atSevere().log(
String.format(
"Could not find registrar for package token %s", packageToken));
}
}
}
});
tm().transact(this::checkPackages);
}
private void checkPackages() {
ImmutableList<PackagePromotion> packages = tm().loadAllOf(PackagePromotion.class);
ImmutableList.Builder<PackagePromotion> packagesOverCreateLimitBuilder =
new ImmutableList.Builder<>();
ImmutableList.Builder<PackagePromotion> packagesOverActiveDomainsLimitBuilder =
new ImmutableList.Builder<>();
for (PackagePromotion packagePromo : packages) {
Long creates =
(Long)
tm().query(
"SELECT COUNT(*) FROM DomainHistory WHERE current_package_token ="
+ " :token AND modificationTime >= :lastBilling AND type ="
+ " 'DOMAIN_CREATE'")
.setParameter("token", packagePromo.getToken().getKey().toString())
.setParameter("lastBilling", packagePromo.getNextBillingDate().minusYears(1))
.getSingleResult();
if (creates > packagePromo.getMaxCreates()) {
int overage = Ints.saturatedCast(creates) - packagePromo.getMaxCreates();
logger.atInfo().log(
"Package with package token %s has exceeded their max domain creation limit"
+ " by %d name(s).",
packagePromo.getToken().getKey(), overage);
packagesOverCreateLimitBuilder.add(packagePromo);
}
Long activeDomains =
tm().query(
"SELECT COUNT(*) FROM Domain WHERE currentPackageToken = :token"
+ " AND deletionTime = :endOfTime",
Long.class)
.setParameter("token", packagePromo.getToken())
.setParameter("endOfTime", END_OF_TIME)
.getSingleResult();
if (activeDomains > packagePromo.getMaxDomains()) {
int overage = Ints.saturatedCast(activeDomains) - packagePromo.getMaxDomains();
logger.atInfo().log(
"Package with package token %s has exceed their max active domains limit by"
+ " %d name(s).",
packagePromo.getToken().getKey(), overage);
packagesOverActiveDomainsLimitBuilder.add(packagePromo);
}
}
handlePackageCreationOverage(packagesOverCreateLimitBuilder.build());
handleActiveDomainOverage(packagesOverActiveDomainsLimitBuilder.build());
}
private void handlePackageCreationOverage(ImmutableList<PackagePromotion> overageList) {
if (overageList.isEmpty()) {
logger.atInfo().log("Found no packages over their create limit.");
return;
}
logger.atInfo().log("Found %d packages over their create limit.", overageList.size());
for (PackagePromotion packagePromotion : overageList) {
AllocationToken packageToken = tm().loadByKey(packagePromotion.getToken());
Optional<Registrar> registrar =
Registrar.loadByRegistrarIdCached(
Iterables.getOnlyElement(packageToken.getAllowedRegistrarIds()));
if (registrar.isPresent()) {
String body =
String.format(
packageCreateLimitEmailBody,
registrar.get().getRegistrarName(),
packageToken.getToken(),
registrySupportEmail);
sendNotification(packageToken, packageCreateLimitEmailSubject, body, registrar.get());
} else {
throw new IllegalStateException(
String.format("Could not find registrar for package token %s", packageToken));
}
}
}
private void handleActiveDomainOverage(ImmutableList<PackagePromotion> overageList) {
if (overageList.isEmpty()) {
logger.atInfo().log("Found no packages over their active domains limit.");
return;
}
logger.atInfo().log("Found %d packages over their active domains limit.", overageList.size());
for (PackagePromotion packagePromotion : overageList) {
int daysSinceLastNotification =
packagePromotion
.getLastNotificationSent()
.map(sentDate -> Days.daysBetween(sentDate, clock.nowUtc()).getDays())
.orElse(Integer.MAX_VALUE);
if (daysSinceLastNotification < THIRTY_DAYS) {
// Don't send an email if notification was already sent within the last 30
// days
continue;
} else if (daysSinceLastNotification < FORTY_DAYS) {
// Send an upgrade email if last email was between 30 and 40 days ago
sendActiveDomainOverageEmail(/* warning= */ false, packagePromotion);
} else {
// Send a warning email
sendActiveDomainOverageEmail(/* warning= */ true, packagePromotion);
}
}
}
private void sendActiveDomainOverageEmail(boolean warning, PackagePromotion packagePromotion) {
String emailSubject =
warning ? packageDomainLimitWarningEmailSubject : packageDomainLimitUpgradeEmailSubject;
String emailTemplate =
warning ? packageDomainLimitWarningEmailBody : packageDomainLimitUpgradeEmailBody;
AllocationToken packageToken = tm().loadByKey(packagePromotion.getToken());
Optional<Registrar> registrar =
Registrar.loadByRegistrarIdCached(
Iterables.getOnlyElement(packageToken.getAllowedRegistrarIds()));
if (registrar.isPresent()) {
String body =
String.format(
emailTemplate,
registrar.get().getRegistrarName(),
packageToken.getToken(),
registrySupportEmail);
sendNotification(packageToken, emailSubject, body, registrar.get());
tm().put(packagePromotion.asBuilder().setLastNotificationSent(clock.nowUtc()).build());
} else {
throw new IllegalStateException(
String.format("Could not find registrar for package token %s", packageToken));
}
}
private void sendNotification(
@@ -26,6 +26,7 @@ import google.registry.beam.common.RegistryJpaIO;
import google.registry.beam.common.RegistryJpaIO.Read;
import google.registry.beam.spec11.SafeBrowsingTransforms.EvaluateSafeBrowsingFn;
import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.model.IdService;
import google.registry.model.domain.Domain;
import google.registry.model.reporting.Spec11ThreatMatch;
import google.registry.model.reporting.Spec11ThreatMatch.ThreatType;
@@ -45,6 +46,7 @@ import org.apache.beam.sdk.transforms.DoFn;
import org.apache.beam.sdk.transforms.GroupByKey;
import org.apache.beam.sdk.transforms.MapElements;
import org.apache.beam.sdk.transforms.ParDo;
import org.apache.beam.sdk.transforms.Reshuffle;
import org.apache.beam.sdk.values.KV;
import org.apache.beam.sdk.values.PCollection;
import org.apache.beam.sdk.values.TypeDescriptor;
@@ -154,25 +156,36 @@ public class Spec11Pipeline implements Serializable {
static void saveToSql(
PCollection<KV<DomainNameInfo, ThreatMatch>> threatMatches, Spec11PipelineOptions options) {
String transformId = "Spec11 Threat Matches";
LocalDate date = LocalDate.parse(options.getDate(), ISODateTimeFormat.date());
threatMatches.apply(
"Write to Sql: " + transformId,
RegistryJpaIO.<KV<DomainNameInfo, ThreatMatch>>write()
.withName(transformId)
.withBatchSize(options.getSqlWriteBatchSize())
.withJpaConverter(
(kv) -> {
DomainNameInfo domainNameInfo = kv.getKey();
return new Spec11ThreatMatch.Builder()
.setThreatTypes(
ImmutableSet.of(ThreatType.valueOf(kv.getValue().threatType())))
.setCheckDate(date)
.setDomainName(domainNameInfo.domainName())
.setDomainRepoId(domainNameInfo.domainRepoId())
.setRegistrarId(domainNameInfo.registrarId())
.build();
}));
String transformId = "Spec11 Threat Matches";
threatMatches
.apply(
"Construct objects",
ParDo.of(
new DoFn<KV<DomainNameInfo, ThreatMatch>, Spec11ThreatMatch>() {
@ProcessElement
public void processElement(
@Element KV<DomainNameInfo, ThreatMatch> input,
OutputReceiver<Spec11ThreatMatch> output) {
Spec11ThreatMatch spec11ThreatMatch =
new Spec11ThreatMatch.Builder()
.setThreatTypes(
ImmutableSet.of(ThreatType.valueOf(input.getValue().threatType())))
.setCheckDate(date)
.setDomainName(input.getKey().domainName())
.setDomainRepoId(input.getKey().domainRepoId())
.setRegistrarId(input.getKey().registrarId())
.setId(IdService.allocateId())
.build();
output.output(spec11ThreatMatch);
}
}))
.apply("Prevent Fusing", Reshuffle.viaRandomKey())
.apply(
"Write to Sql: " + transformId,
RegistryJpaIO.<Spec11ThreatMatch>write()
.withName(transformId)
.withBatchSize(options.getSqlWriteBatchSize()));
}
static void saveToGcs(
@@ -1317,15 +1317,41 @@ public final class RegistryConfig {
}
@Provides
@Config("packageCreateLimitEmailSubjectText")
public static String providePackageCreateLimitEmailSubjectText(RegistryConfigSettings config) {
return config.packageMonitoring.packageCreateLimitEmailSubjectText;
@Config("packageCreateLimitEmailSubject")
public static String providePackageCreateLimitEmailSubject(RegistryConfigSettings config) {
return config.packageMonitoring.packageCreateLimitEmailSubject;
}
@Provides
@Config("packageCreateLimitEmailBodyText")
public static String providePackageCreateLimitEmailBodyText(RegistryConfigSettings config) {
return config.packageMonitoring.packageCreateLimitEmailBodyText;
@Config("packageCreateLimitEmailBody")
public static String providePackageCreateLimitEmailBody(RegistryConfigSettings config) {
return config.packageMonitoring.packageCreateLimitEmailBody;
}
@Provides
@Config("packageDomainLimitWarningEmailSubject")
public static String providePackageDomainLimitWarningEmailSubject(
RegistryConfigSettings config) {
return config.packageMonitoring.packageDomainLimitWarningEmailSubject;
}
@Provides
@Config("packageDomainLimitWarningEmailBody")
public static String providePackageDomainLimitWarningEmailBody(RegistryConfigSettings config) {
return config.packageMonitoring.packageDomainLimitWarningEmailBody;
}
@Provides
@Config("packageDomainLimitUpgradeEmailSubject")
public static String providePackageDomainLimitUpgradeEmailSubject(
RegistryConfigSettings config) {
return config.packageMonitoring.packageDomainLimitUpgradeEmailSubject;
}
@Provides
@Config("packageDomainLimitUpgradeEmailBody")
public static String providePackageDomainLimitUpgradeEmailBody(RegistryConfigSettings config) {
return config.packageMonitoring.packageDomainLimitUpgradeEmailBody;
}
}
@@ -255,7 +255,11 @@ public class RegistryConfigSettings {
/** Configuration for package compliance monitoring. */
public static class PackageMonitoring {
public String packageCreateLimitEmailSubjectText;
public String packageCreateLimitEmailBodyText;
public String packageCreateLimitEmailSubject;
public String packageCreateLimitEmailBody;
public String packageDomainLimitWarningEmailSubject;
public String packageDomainLimitWarningEmailBody;
public String packageDomainLimitUpgradeEmailSubject;
public String packageDomainLimitUpgradeEmailBody;
}
}
@@ -536,9 +536,9 @@ sslCertificateValidation:
# Configuration options for the package compliance monitoring
packageMonitoring:
# Email subject text to notify partners their package has exceeded the limit for domain creates
packageCreateLimitEmailSubjectText: "NOTICE: Your Package Is Being Upgraded"
packageCreateLimitEmailSubject: "NOTICE: Your package is being upgraded"
# Email body text template notify partners their package has exceeded the limit for domain creates
packageCreateLimitEmailBodyText: >
packageCreateLimitEmailBody: >
Dear %1$s,
We are contacting you to inform you that your package with the package token
@@ -550,3 +550,37 @@ packageMonitoring:
Regards,
Example Registry
# Email subject text to notify partners their package has exceeded the limit for current active domains and warn them their package will be upgraded in 30 days
packageDomainLimitWarningEmailSubject: "WARNING: Your package has exceeded the domain limit"
# Email body text template to warn partners their package has exceeded the limit for active domains and will be upgraded in 30 days
packageDomainLimitWarningEmailBody: >
Dear %1$s,
We are contacting you to inform you that your package with the package token
%2$s has exceeded its limit for active domains.
Your package will be upgraded to the next tier in 30 days if the number of active domains does not return below the limit.
If you have any questions or require additional support, please contact us
at %3$s.
Regards,
Example Registry
# Email subject text to notify partners their package has exceeded the limit
# for current active domains for more than 30 days and will be upgraded
packageDomainLimitUpgradeEmailSubject: "NOTICE: Your package is being upgraded"
# Email body text template to warn partners their package has exceeded the
# limit for active domains for more than 30 days and will be upgraded
packageDomainLimitUpgradeEmailBody: >
Dear %1$s,
We are contacting you to inform you that your package with the package token
%2$s has exceeded its limit for active domains.
Your package will now be upgraded to the next tier.
If you have any questions or require additional support, please contact us
at %3$s.
Regards,
Example Registry
@@ -175,16 +175,6 @@
<url-pattern>/_dr/cron/fanout</url-pattern>
</servlet-mapping>
<!-- Pipeline GUI servlets. -->
<servlet>
<servlet-name>pipeline</servlet-name>
<servlet-class>com.google.appengine.tools.pipeline.impl.servlets.PipelineServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pipeline</servlet-name>
<url-pattern>/_ah/pipeline/*</url-pattern>
</servlet-mapping>
<!-- Syncs registrars to the registrar spreadsheet. -->
<servlet-mapping>
<servlet-name>backend-servlet</servlet-name>
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.util.DomainNameUtils;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -39,7 +40,7 @@ import org.joda.time.LocalDate;
@Index(name = "spec11threatmatch_tld_idx", columnList = "tld"),
@Index(name = "spec11threatmatch_check_date_idx", columnList = "checkDate")
})
public class Spec11ThreatMatch extends ImmutableObject implements Buildable {
public class Spec11ThreatMatch extends ImmutableObject implements Buildable, Serializable {
/** The type of threat detected. */
public enum ThreatType {
@@ -57,7 +57,13 @@ public class CheckPackagesComplianceActionTest {
// This is the default creation time for test data.
private final FakeClock clock = new FakeClock(DateTime.parse("2012-03-25TZ"));
private static final String CREATE_LIMIT_EMAIL_SUBJECT = "create limit subject";
private static final String DOMAIN_LIMIT_WARNING_EMAIL_SUBJECT = "domain limit warning subject";
private static final String DOMAIN_LIMIT_UPGRADE_EMAIL_SUBJECT = "domain limit upgrade subject";
private static final String CREATE_LIMIT_EMAIL_BODY = "create limit body %1$s %2$s %3$s";
private static final String DOMAIN_LIMIT_WARNING_EMAIL_BODY =
"domain limit warning body %1$s %2$s %3$s";
private static final String DOMAIN_LIMIT_UPGRADE_EMAIL_BODY =
"domain limit upgrade body %1$s %2$s %3$s";
private static final String SUPPORT_EMAIL = "registry@test.com";
@RegisterExtension
@@ -70,7 +76,6 @@ public class CheckPackagesComplianceActionTest {
private final Logger loggerToIntercept =
Logger.getLogger(CheckPackagesComplianceAction.class.getCanonicalName());
private final SendEmailService emailService = mock(SendEmailService.class);
private Contact contact;
private PackagePromotion packagePromotion;
private SendEmailUtils sendEmailUtils;
@@ -88,7 +93,15 @@ public class CheckPackagesComplianceActionTest {
createTld("tld");
action =
new CheckPackagesComplianceAction(
sendEmailUtils, CREATE_LIMIT_EMAIL_SUBJECT, CREATE_LIMIT_EMAIL_BODY, SUPPORT_EMAIL);
sendEmailUtils,
clock,
CREATE_LIMIT_EMAIL_SUBJECT,
DOMAIN_LIMIT_WARNING_EMAIL_SUBJECT,
DOMAIN_LIMIT_UPGRADE_EMAIL_SUBJECT,
CREATE_LIMIT_EMAIL_BODY,
DOMAIN_LIMIT_WARNING_EMAIL_BODY,
DOMAIN_LIMIT_UPGRADE_EMAIL_BODY,
SUPPORT_EMAIL);
token =
persistResource(
new AllocationToken.Builder()
@@ -110,7 +123,6 @@ public class CheckPackagesComplianceActionTest {
.setLastNotificationSent(DateTime.parse("2010-11-12T05:00:00Z"))
.build();
tm().transact(() -> tm().put(packagePromotion));
contact = persistActiveContact("contact1234");
}
@@ -121,6 +133,7 @@ public class CheckPackagesComplianceActionTest {
@Test
void testSuccess_noPackageOverCreateLimit() {
tm().transact(() -> tm().put(packagePromotion));
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
.asBuilder()
@@ -136,6 +149,7 @@ public class CheckPackagesComplianceActionTest {
@Test
void testSuccess_onePackageOverCreateLimit() throws Exception {
tm().transact(() -> tm().put(packagePromotion));
// Create limit is 1, creating 2 domains to go over the limit
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
@@ -168,6 +182,7 @@ public class CheckPackagesComplianceActionTest {
@Test
void testSuccess_multiplePackagesOverCreateLimit() {
tm().transact(() -> tm().put(packagePromotion));
// Create limit is 1, creating 2 domains to go over the limit
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
@@ -234,6 +249,7 @@ public class CheckPackagesComplianceActionTest {
@Test
void testSuccess_onlyChecksCurrentBillingYear() {
tm().transact(() -> tm().put(packagePromotion));
AllocationToken token2 =
persistResource(
new AllocationToken.Builder()
@@ -273,4 +289,278 @@ public class CheckPackagesComplianceActionTest {
.hasLogAtLevelWithMessage(Level.INFO, "Found no packages over their create limit.");
verifyNoInteractions(emailService);
}
@Test
void testSuccess_noPackageOverActiveDomainsLimit() {
tm().transact(() -> tm().put(packagePromotion));
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
action.run();
verifyNoInteractions(emailService);
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Found no packages over their active domains limit.");
}
@Test
void testSuccess_onePackageOverActiveDomainsLimit() {
packagePromotion = packagePromotion.asBuilder().setMaxCreates(4).setMaxDomains(1).build();
tm().transact(() -> tm().put(packagePromotion));
// Domains limit is 1, creating 2 domains to go over the limit
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
persistEppResource(
DatabaseHelper.newDomain("buzz.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
AllocationToken token2 =
persistResource(
new AllocationToken.Builder()
.setToken("token")
.setTokenType(TokenType.PACKAGE)
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setAllowedTlds(ImmutableSet.of("foo"))
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
.setDiscountFraction(1)
.build());
PackagePromotion packagePromotion2 =
new PackagePromotion.Builder()
.setToken(token2)
.setMaxDomains(8)
.setMaxCreates(4)
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
.build();
tm().transact(() -> tm().put(packagePromotion2));
persistEppResource(
DatabaseHelper.newDomain("foo2.tld", contact)
.asBuilder()
.setCurrentPackageToken(token2.createVKey())
.build());
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.INFO,
"Package with package token abc123 has exceed their max active domains limit by 1"
+ " name(s).");
verify(emailService).sendEmail(emailCaptor.capture());
EmailMessage emailMessage = emailCaptor.getValue();
assertThat(emailMessage.subject()).isEqualTo(DOMAIN_LIMIT_WARNING_EMAIL_SUBJECT);
assertThat(emailMessage.body())
.isEqualTo(
String.format(
DOMAIN_LIMIT_WARNING_EMAIL_BODY, "The Registrar", "abc123", SUPPORT_EMAIL));
PackagePromotion packageAfterCheck =
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
assertThat(packageAfterCheck.getLastNotificationSent().get()).isEqualTo(clock.nowUtc());
}
@Test
void testSuccess_multiplePackagesOverActiveDomainsLimit() {
tm().transact(
() -> tm().put(packagePromotion.asBuilder().setMaxDomains(1).setMaxCreates(4).build()));
// Domains limit is 1, creating 2 domains to go over the limit
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
persistEppResource(
DatabaseHelper.newDomain("buzz.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
AllocationToken token2 =
persistResource(
new AllocationToken.Builder()
.setToken("token")
.setTokenType(TokenType.PACKAGE)
.setCreationTimeForTest(DateTime.parse("2010-11-12T05:00:00Z"))
.setAllowedTlds(ImmutableSet.of("foo"))
.setAllowedRegistrarIds(ImmutableSet.of("TheRegistrar"))
.setRenewalPriceBehavior(RenewalPriceBehavior.SPECIFIED)
.setDiscountFraction(1)
.build());
PackagePromotion packagePromotion2 =
new PackagePromotion.Builder()
.setToken(token2)
.setMaxDomains(1)
.setMaxCreates(5)
.setPackagePrice(Money.of(CurrencyUnit.USD, 1000))
.setNextBillingDate(DateTime.parse("2012-11-12T05:00:00Z"))
.build();
tm().transact(() -> tm().put(packagePromotion2));
persistEppResource(
DatabaseHelper.newDomain("foo2.tld", contact)
.asBuilder()
.setCurrentPackageToken(token2.createVKey())
.build());
persistEppResource(
DatabaseHelper.newDomain("buzz2.tld", contact)
.asBuilder()
.setCurrentPackageToken(token2.createVKey())
.build());
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Found 2 packages over their active domains limit.");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.INFO,
"Package with package token abc123 has exceed their max active domains limit by 1"
+ " name(s).");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.INFO,
"Package with package token token has exceed their max active domains limit by 1"
+ " name(s).");
verify(emailService, times(2)).sendEmail(any(EmailMessage.class));
}
@Test
void testSuccess_packageOverActiveDomainsLimitAlreadySentWarningEmail_DoesNotSendAgain() {
packagePromotion =
packagePromotion
.asBuilder()
.setMaxCreates(4)
.setMaxDomains(1)
.setLastNotificationSent(clock.nowUtc().minusDays(5))
.build();
tm().transact(() -> tm().put(packagePromotion));
// Domains limit is 1, creating 2 domains to go over the limit
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
persistEppResource(
DatabaseHelper.newDomain("buzz.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.INFO,
"Package with package token abc123 has exceed their max active domains limit by 1"
+ " name(s).");
verifyNoInteractions(emailService);
PackagePromotion packageAfterCheck =
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
assertThat(packageAfterCheck.getLastNotificationSent().get())
.isEqualTo(clock.nowUtc().minusDays(5));
}
@Test
void testSuccess_packageOverActiveDomainsLimitAlreadySentWarningEmailOver40DaysAgo_SendsAgain() {
packagePromotion =
packagePromotion
.asBuilder()
.setMaxCreates(4)
.setMaxDomains(1)
.setLastNotificationSent(clock.nowUtc().minusDays(45))
.build();
tm().transact(() -> tm().put(packagePromotion));
// Domains limit is 1, creating 2 domains to go over the limit
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
persistEppResource(
DatabaseHelper.newDomain("buzz.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.INFO,
"Package with package token abc123 has exceed their max active domains limit by 1"
+ " name(s).");
verify(emailService).sendEmail(emailCaptor.capture());
EmailMessage emailMessage = emailCaptor.getValue();
assertThat(emailMessage.subject()).isEqualTo(DOMAIN_LIMIT_WARNING_EMAIL_SUBJECT);
assertThat(emailMessage.body())
.isEqualTo(
String.format(
DOMAIN_LIMIT_WARNING_EMAIL_BODY, "The Registrar", "abc123", SUPPORT_EMAIL));
PackagePromotion packageAfterCheck =
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
assertThat(packageAfterCheck.getLastNotificationSent().get()).isEqualTo(clock.nowUtc());
}
@Test
void testSuccess_packageOverActiveDomainsLimitAlreadySentWarning30DaysAgo_SendsUpgradeEmail() {
packagePromotion =
packagePromotion
.asBuilder()
.setMaxCreates(4)
.setMaxDomains(1)
.setLastNotificationSent(clock.nowUtc().minusDays(31))
.build();
tm().transact(() -> tm().put(packagePromotion));
// Domains limit is 1, creating 2 domains to go over the limit
persistEppResource(
DatabaseHelper.newDomain("foo.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
persistEppResource(
DatabaseHelper.newDomain("buzz.tld", contact)
.asBuilder()
.setCurrentPackageToken(token.createVKey())
.build());
action.run();
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(Level.INFO, "Found 1 packages over their active domains limit.");
assertAboutLogs()
.that(logHandler)
.hasLogAtLevelWithMessage(
Level.INFO,
"Package with package token abc123 has exceed their max active domains limit by 1"
+ " name(s).");
verify(emailService).sendEmail(emailCaptor.capture());
EmailMessage emailMessage = emailCaptor.getValue();
assertThat(emailMessage.subject()).isEqualTo(DOMAIN_LIMIT_UPGRADE_EMAIL_SUBJECT);
assertThat(emailMessage.body())
.isEqualTo(
String.format(
DOMAIN_LIMIT_UPGRADE_EMAIL_BODY, "The Registrar", "abc123", SUPPORT_EMAIL));
PackagePromotion packageAfterCheck =
tm().transact(() -> PackagePromotion.loadByTokenString(token.getToken()).get());
assertThat(packageAfterCheck.getLastNotificationSent().get()).isEqualTo(clock.nowUtc());
}
}
@@ -280,9 +280,9 @@ class Spec11PipelineTest {
private void verifySaveToCloudSql() {
tm().transact(
() -> {
ImmutableList<Spec11ThreatMatch> sqlThreatMatches =
ImmutableList<Spec11ThreatMatch> spec11ThreatMatches =
Spec11ThreatMatchDao.loadEntriesByDate(tm(), new LocalDate(2020, 1, 27));
assertThat(sqlThreatMatches)
assertThat(spec11ThreatMatches)
.comparingElementsUsing(immutableObjectCorrespondence("id"))
.containsExactlyElementsIn(sqlThreatMatches);
});