1
0
mirror of https://github.com/google/nomulus synced 2026-05-25 09:10:51 +00:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Lai Jiang
8c1b178c94 Add more options to the generate escrow deposit command (#1453)
This adds two new options:

1) An option to run RDE in lenient mode.
2) An option to run RDE with the new Beam pipeline regardless of the datastore setting.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1453)
<!-- Reviewable:end -->
2021-12-10 22:25:32 -05:00
Michael Muller
fe0353ae7d Remove unused variable "random" (#1458)
Remove variable that was rendered unused in commit
5158673f21 (consolidate all registry/TLD
classes)
2021-12-10 15:10:39 -05:00
gbrodman
360c198f4f Copy into PersistentSets in Domains if applicable (#1457)
* Copy into PersistentSets in Domains if applicable

This is similar to https://github.com/google/nomulus/pull/1456

It is possible that in some cases we could get an exception:

Caused by: org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: [parent]

The main cause of this, according to research (StackOverflow :P) is that
when Hibernate is calling the setters for these sets of children it's
losing the connection to the previously-managed child entity (which it
needs, in order to know how to delete orphans). Thus, the solution is to
maintain the same instance of the persistent set and just add/remove
to/from it as necessary.

This is complicated by the fact that sometimes the setter is given the
persistent set (the one we want to keep) and sometimes (?) it isn't.
2021-12-10 14:14:54 -05:00
gbrodman
e79e76e578 Copy DTRs into PersistentSet field if applicable in HistoryEntry (#1456)
In replay (and possibly in other cases) we're getting an exception:

Caused by: org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: google.registry.model.domain.DomainHistory.internalDomainTransactionRecords

The main cause of this, according to research (StackOverflow :P) is that
when Hibernate is calling the setters for these sets of children it's
losing the connection to the previously-managed child entity (which it
needs, in order to know how to delete orphans). Thus, the solution is to
maintain the same instance of the persistent set and just add/remove
to/from it as necessary.

This is complicated by the fact that sometimes the setter is given the
persistent set (the one we want to keep) and sometimes (?) it isn't. We
will need to try this out to be sure.
2021-12-09 11:56:58 -05:00
Ben McIlwain
a5dbfceae1 Allow command to enqueue poll messages for multiple registrars (#1446)
* Allow command to enqueue poll messages for multiple registrars
2021-12-08 16:33:28 -05:00
gbrodman
647d6a1b08 Update task-tree dependency (#1452)
see https://b.corp.google.com/issues/208629747 for details; this brings
in an old Gradle version as a transitive dependency

Version 2.x of the task-tree plugin uses Gradle 6.8 (or higher)
2021-12-07 17:03:46 -05:00
Lai Jiang
ec417e9258 Truncate web WHOIS request path when recording metrics (#1451)
The cardinality for the paths is unbound, and could generate a huge
amount of metrics if someone is scanning our web WHOIS endpoint.

See b/209488119 for an example of such a sudden increase in metric volume.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/google/nomulus/1451)
<!-- Reviewable:end -->
2021-12-07 17:01:29 -05:00
Rachel Guan
55bef58063 Use simple class name of a class in stringify() (#1435)
* Keep CLASS_REGISTRY and CLASS_NAME_REGISTRY up to date

* Use simple class name in vkey string
2021-12-07 11:49:50 -05:00
Rachel Guan
ecc3d9f75d Extract CLASS_REGISTRY from VKeyTranslatorFactory (#1440)
* Add annotation for unit test file

* Extract CLASS_REGISTRY from VKeyTranslatorFactory

* Improve test cases and docs
2021-12-06 16:41:17 -05:00
gbrodman
f23d2ca315 Add replicateToDatastore to non-prod cron files (#1450)
* Add replicateToDatastore to non-prod cron files

This shouldn't do anything yet (since ReplicateToDatastoreAction checks the
migration state before doing anything) but we'll want to have this in
place.
2021-12-06 12:54:55 -05:00
26 changed files with 587 additions and 87 deletions

View File

@@ -46,7 +46,7 @@ plugins {
id 'com.diffplug.gradle.spotless' version '3.25.0'
id 'jacoco'
id 'com.dorongold.task-tree' version '1.5'
id 'com.dorongold.task-tree' version '2.1.0'
}
node {

View File

@@ -48,7 +48,6 @@ import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.CloudTasksUtils;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Stream;
import javax.inject.Inject;
@@ -97,8 +96,6 @@ public final class TldFanoutAction implements Runnable {
EXCLUDE_PARAM,
JITTER_SECONDS_PARAM);
private static final Random random = new Random();
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject Clock clock;

View File

@@ -208,6 +208,15 @@
<target>backend</target>
</cron>
<cron>
<url><![CDATA[/_dr/cron/replicateToDatastore]]></url>
<description>
Replays recent transactions from SQL to the Datastore secondary backend.
</description>
<schedule>every 3 minutes</schedule>
<target>backend</target>
</cron>
<cron>
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
<description>

View File

@@ -211,6 +211,15 @@
</cron>
-->
<cron>
<url><![CDATA[/_dr/cron/replicateToDatastore]]></url>
<description>
Replays recent transactions from SQL to the Datastore secondary backend.
</description>
<schedule>every 3 minutes</schedule>
<target>backend</target>
</cron>
<!--
The next two wipeout jobs are required when crash has production data.
-->

View File

@@ -120,6 +120,15 @@
<target>backend</target>
</cron>
<cron>
<url><![CDATA[/_dr/cron/replicateToDatastore]]></url>
<description>
Replays recent transactions from SQL to the Datastore secondary backend.
</description>
<schedule>every 3 minutes</schedule>
<target>backend</target>
</cron>
<cron>
<url><![CDATA[/_dr/cron/commitLogCheckpoint]]></url>
<description>

View File

@@ -247,6 +247,15 @@
<target>backend</target>
</cron>
<cron>
<url><![CDATA[/_dr/cron/replicateToDatastore]]></url>
<description>
Replays recent transactions from SQL to the Datastore secondary backend.
</description>
<schedule>every 3 minutes</schedule>
<target>backend</target>
</cron>
<cron>
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
<description>

View File

@@ -0,0 +1,64 @@
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.model.common;
import static com.google.common.base.Functions.identity;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.EntityClasses.ALL_CLASSES;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.annotation.EntitySubclass;
import java.util.Map;
import java.util.stream.Collectors;
/** A helper to manage class name and class path mapping. */
public class ClassPathManager {
/**
* Class registry allowing us to restore the original class object from the unqualified class
* name, which is all the datastore key gives us. Note that entities annotated
* with @EntitySubclass are removed because they share the same kind of the key with their parent
* class.
*/
public static final Map<String, Class<?>> CLASS_REGISTRY =
ALL_CLASSES.stream()
.filter(clazz -> !clazz.isAnnotationPresent(EntitySubclass.class))
.collect(Collectors.toMap(com.googlecode.objectify.Key::getKind, identity()));
/**
* Class name registry allowing us to obtain the class name the unqualified class, which is all
* the datastore key gives us. Note that entities annotated with @EntitySubclass are removed
* because they share the same kind of the key with their parent class.
*/
public static final Map<Class<?>, String> CLASS_NAME_REGISTRY =
ALL_CLASSES.stream()
.filter(clazz -> !clazz.isAnnotationPresent(EntitySubclass.class))
.collect(Collectors.toMap(identity(), com.googlecode.objectify.Key::getKind));
@VisibleForTesting
public static void addTestEntityClass(Class<?> clazz) {
CLASS_REGISTRY.put(clazz.getSimpleName(), clazz);
CLASS_NAME_REGISTRY.put(clazz, clazz.getSimpleName());
}
public static <T> Class<T> getClass(String className) {
checkArgument(CLASS_REGISTRY.containsKey(className), "Class not found in class registry");
return (Class<T>) CLASS_REGISTRY.get(className);
}
public static <T> String getClassName(Class<T> clazz) {
checkArgument(CLASS_NAME_REGISTRY.containsKey(clazz), "Class not found in class name registry");
return CLASS_NAME_REGISTRY.get(clazz);
}
}

View File

@@ -86,6 +86,7 @@ import javax.persistence.Embedded;
import javax.persistence.MappedSuperclass;
import javax.persistence.PostLoad;
import javax.persistence.Transient;
import org.hibernate.collection.internal.PersistentSet;
import org.joda.time.DateTime;
import org.joda.time.Interval;
@@ -508,16 +509,37 @@ public class DomainContent extends EppResource
this.nsHosts = forceEmptyToNull(nsHosts);
}
// Note: for the two methods below, how we wish to treat the Hibernate setters depends on the
// current state of the object and what's passed in. The key principle is that we wish to maintain
// the link between parent and child objects, meaning that we should keep around whichever of the
// two sets (the parameter vs the class variable and clear/populate that as appropriate.
//
// If the class variable is a PersistentSet and we overwrite it here, Hibernate will throw
// an exception "A collection with cascade=”all-delete-orphan” was no longer referenced by the
// owning entity instance". See https://stackoverflow.com/questions/5587482 for more details.
// Hibernate needs this in order to populate gracePeriods but no one else should ever use it
@SuppressWarnings("UnusedMethod")
private void setInternalGracePeriods(Set<GracePeriod> gracePeriods) {
this.gracePeriods = gracePeriods;
if (this.gracePeriods instanceof PersistentSet) {
Set<GracePeriod> nonNullGracePeriods = nullToEmpty(gracePeriods);
this.gracePeriods.retainAll(nonNullGracePeriods);
this.gracePeriods.addAll(nonNullGracePeriods);
} else {
this.gracePeriods = gracePeriods;
}
}
// Hibernate needs this in order to populate dsData but no one else should ever use it
@SuppressWarnings("UnusedMethod")
private void setInternalDelegationSignerData(Set<DelegationSignerData> dsData) {
this.dsData = dsData;
if (this.dsData instanceof PersistentSet) {
Set<DelegationSignerData> nonNullDsData = nullToEmpty(dsData);
this.dsData.retainAll(nonNullDsData);
this.dsData.addAll(nonNullDsData);
} else {
this.dsData = dsData;
}
}
public final String getCurrentSponsorRegistrarId() {

View File

@@ -209,7 +209,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
@SuppressWarnings("unused")
private void setInternalDomainTransactionRecords(
Set<DomainTransactionRecord> domainTransactionRecords) {
this.domainTransactionRecords = domainTransactionRecords;
super.setDomainTransactionRecords(domainTransactionRecords);
}
@Id

View File

@@ -16,6 +16,7 @@ package google.registry.model.reporting;
import static com.google.common.base.Preconditions.checkArgument;
import static com.googlecode.objectify.Key.getKind;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
@@ -63,6 +64,7 @@ import javax.persistence.Enumerated;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
import org.apache.commons.lang3.BooleanUtils;
import org.hibernate.collection.internal.PersistentSet;
import org.joda.time.DateTime;
/**
@@ -317,9 +319,23 @@ public class HistoryEntry extends ImmutableObject
/** This method exists solely to satisfy Hibernate. Use the {@link Builder} instead. */
@SuppressWarnings("UnusedMethod")
private void setDomainTransactionRecords(Set<DomainTransactionRecord> domainTransactionRecords) {
this.domainTransactionRecords =
domainTransactionRecords == null ? null : ImmutableSet.copyOf(domainTransactionRecords);
protected void setDomainTransactionRecords(
Set<DomainTransactionRecord> domainTransactionRecords) {
// Note: how we wish to treat this Hibernate setter depends on the current state of the object
// and what's passed in. The key principle is that we wish to maintain the link between parent
// and child objects, meaning that we should keep around whichever of the two sets (the
// parameter vs the class variable and clear/populate that as appropriate.
//
// If the class variable is a PersistentSet and we overwrite it here, Hibernate will throw
// an exception "A collection with cascade=”all-delete-orphan” was no longer referenced by the
// owning entity instance". See https://stackoverflow.com/questions/5587482 for more details.
if (this.domainTransactionRecords instanceof PersistentSet) {
Set<DomainTransactionRecord> nonNullRecords = nullToEmpty(domainTransactionRecords);
this.domainTransactionRecords.retainAll(nonNullRecords);
this.domainTransactionRecords.addAll(nonNullRecords);
} else {
this.domainTransactionRecords = domainTransactionRecords;
}
}
/**

View File

@@ -14,18 +14,13 @@
package google.registry.model.translators;
import static com.google.common.base.Functions.identity;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.EntityClasses.ALL_CLASSES;
import com.google.appengine.api.datastore.Key;
import com.google.common.annotations.VisibleForTesting;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.common.ClassPathManager;
import google.registry.persistence.VKey;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
@@ -36,15 +31,6 @@ import javax.annotation.Nullable;
*/
public class VKeyTranslatorFactory extends AbstractSimpleTranslatorFactory<VKey, Key> {
// Class registry allowing us to restore the original class object from the unqualified class
// name, which is all the datastore key gives us.
// Note that entities annotated with @EntitySubclass are removed because they share the same
// kind of the key with their parent class.
private static final Map<String, Class<?>> CLASS_REGISTRY =
ALL_CLASSES.stream()
.filter(clazz -> !clazz.isAnnotationPresent(EntitySubclass.class))
.collect(Collectors.toMap(com.googlecode.objectify.Key::getKind, identity()));
public VKeyTranslatorFactory() {
super(VKey.class);
}
@@ -67,7 +53,7 @@ public class VKeyTranslatorFactory extends AbstractSimpleTranslatorFactory<VKey,
}
// Try to create the VKey from its reference type.
Class<T> clazz = (Class<T>) CLASS_REGISTRY.get(key.getKind());
Class<T> clazz = ClassPathManager.getClass(key.getKind());
checkArgument(clazz != null, "Unknown Key type: %s", key.getKind());
try {
Method createVKeyMethod =
@@ -92,11 +78,6 @@ public class VKeyTranslatorFactory extends AbstractSimpleTranslatorFactory<VKey,
}
}
@VisibleForTesting
public static void addTestEntityClass(Class<?> clazz) {
CLASS_REGISTRY.put(com.googlecode.objectify.Key.getKind(clazz), clazz);
}
@Override
public SimpleTranslator<VKey, Key> createTranslator() {
return new SimpleTranslator<VKey, Key>() {

View File

@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
import com.googlecode.objectify.Key;
import google.registry.model.BackupGroupRoot;
import google.registry.model.ImmutableObject;
import google.registry.model.common.ClassPathManager;
import google.registry.model.translators.VKeyTranslatorFactory;
import google.registry.util.SerializeUtils;
import java.io.Serializable;
@@ -137,9 +138,9 @@ public class VKey<T> extends ImmutableObject implements Serializable {
*
* <p>Example of a Vkey string by fromWebsafeKey(): "agR0ZXN0chYLEgpEb21haW5CYXNlIgZST0lELTEM"
*
* <p>Example of a vkey string by stringify(): "google.registry.testing.TestObject@sql:rO0ABX" +
* "QAA2Zvbw@ofy:agR0ZXN0cjELEg9FbnRpdHlHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M",
* where sql key and ofy key are values are encoded in Base64.
* <p>Example of a vkey string by stringify(): "kind:TestObject@sql:rO0ABXQAA2Zvbw" +
* "@ofy:agR0ZXN0cjELEg9FbnRpdHlHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M", where sql
* key and ofy key values are encoded in Base64.
*/
public static <T> VKey<T> create(String keyString) throws Exception {
if (!keyString.startsWith(CLASS_TYPE + KV_SEPARATOR)) {
@@ -149,7 +150,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
ImmutableMap<String, String> kvs =
ImmutableMap.copyOf(
Splitter.on(DELIMITER).withKeyValueSeparator(KV_SEPARATOR).split(keyString));
Class classType = Class.forName(kvs.get(CLASS_TYPE));
Class classType = ClassPathManager.getClass(kvs.get(CLASS_TYPE));
if (kvs.containsKey(SQL_LOOKUP_KEY) && kvs.containsKey(OFY_LOOKUP_KEY)) {
return VKey.create(
@@ -290,7 +291,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
/**
* Constructs the string representation of a {@link VKey}.
*
* <p>The string representation of a vkey contains its type, and sql key or ofy key, or both. Each
* <p>The string representation of a vkey contains its kind, and sql key or ofy key, or both. Each
* of the keys is first serialized into a byte array then encoded via Base64 into a web safe
* string.
*
@@ -302,7 +303,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
*/
public String stringify() {
// class type is required to create a vkey
String key = CLASS_TYPE + KV_SEPARATOR + getKind().getName();
String key = CLASS_TYPE + KV_SEPARATOR + ClassPathManager.getClassName(getKind());
if (maybeGetSqlKey().isPresent()) {
key += DELIMITER + SQL_LOOKUP_KEY + KV_SEPARATOR + SerializeUtils.stringify(getSqlKey());
}
@@ -315,7 +316,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
/**
* Constructs the readable string representation of a {@link VKey}.
*
* <p>This readable string representation of a vkey contains its type and its sql key or ofy key,
* <p>This readable string representation of a vkey contains its kind and its sql key or ofy key,
* or both.
*/
@Override

View File

@@ -45,6 +45,7 @@ public abstract class RdeModule {
public static final String PARAM_WATERMARK = "watermark";
public static final String PARAM_WATERMARKS = "watermarks";
public static final String PARAM_MANUAL = "manual";
public static final String PARAM_BEAM = "beam";
public static final String PARAM_DIRECTORY = "directory";
public static final String PARAM_MODE = "mode";
public static final String PARAM_REVISION = "revision";
@@ -72,6 +73,12 @@ public abstract class RdeModule {
return extractBooleanParameter(req, PARAM_MANUAL);
}
@Provides
@Parameter(PARAM_BEAM)
static boolean provideBeam(HttpServletRequest req) {
return extractBooleanParameter(req, PARAM_BEAM);
}
@Provides
@Parameter(PARAM_DIRECTORY)
static Optional<String> provideDirectory(HttpServletRequest req) {

View File

@@ -264,6 +264,11 @@ public final class RdeStagingAction implements Runnable {
@Inject @Config("beamStagingBucketUrl") String stagingBucketUrl;
@Inject @Config("rdeBucket") String rdeBucket;
@Inject @Parameter(RdeModule.PARAM_MANUAL) boolean manual;
@Inject
@Parameter(RdeModule.PARAM_BEAM)
boolean beam;
@Inject @Parameter(RdeModule.PARAM_DIRECTORY) Optional<String> directory;
@Inject @Parameter(RdeModule.PARAM_MODE) ImmutableSet<String> modeStrings;
@Inject @Parameter(RequestParameters.PARAM_TLDS) ImmutableSet<String> tlds;
@@ -289,7 +294,7 @@ public final class RdeStagingAction implements Runnable {
logger.atInfo().log("Pending deposit: %s", pending);
}
ValidationMode validationMode = lenient ? LENIENT : STRICT;
if (tm().isOfy()) {
if (tm().isOfy() && !beam) {
RdeStagingMapper mapper = new RdeStagingMapper(validationMode, pendings);
RdeStagingReducer reducer = reducerFactory.create(validationMode, gcsUtils);
mrRunner
@@ -382,6 +387,9 @@ public final class RdeStagingAction implements Runnable {
if (revision.isPresent()) {
throw new BadRequestException("Revision parameter not allowed in standard operation");
}
if (beam) {
throw new BadRequestException("Beam parameter not allowed in standard operation");
}
return ImmutableSetMultimap.copyOf(
Multimaps.filterValues(

View File

@@ -67,10 +67,16 @@ class RequestMetrics {
private static String truncatePath(String path) {
// We want to bucket RDAP requests by type to use less metric space,
// e.g. "/rdap/domains" rather than "/rdap/domains/foo.tld"
if (path.startsWith("/rdap")) {
List<String> splitPath = Splitter.on("/").omitEmptyStrings().splitToList(path);
return Streams.stream(Iterables.limit(splitPath, 2))
.collect(Collectors.joining("/", "/", "/"));
// Similarly, we put all web WHOIS requests under the same path because otherwise its
// cardinality is unbound, and it is possible to generate a huge amount of metrics with all
// the different paths.
} else if (path.startsWith("/whois")) {
return "/whois";
}
return path;
}

View File

@@ -18,14 +18,21 @@ import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.reporting.HistoryEntry.Type.SYNTHETIC;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.isNullOrEmpty;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.poll.PollMessage;
import google.registry.model.registrar.Registrar;
import google.registry.model.reporting.HistoryEntry;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
/**
* Tool to enqueue a poll message for a registrar.
@@ -54,14 +61,26 @@ class EnqueuePollMessageCommand extends MutatingCommand {
String domainName;
@Parameter(
names = {"-c", "--client"},
names = {"-c", "--clients"},
description =
"Client identifier of the registrar to send the poll message to, if not the owning"
+ " registrar of the domain")
String clientId;
"Comma-delimited list of the client identifier(s) of the registrar(s) to send the poll"
+ " message to, if not the owning registrar of the domain")
List<String> clientIds;
@Parameter(
names = {"-a", "--all"},
description = "Whether to send the message to all real registrars",
arity = 1)
boolean sendToAll;
@Inject
@Config("registryAdminClientId")
String registryAdminClientId;
@Override
protected final void init() {
checkArgument(
!sendToAll || isNullOrEmpty(clientIds), "Cannot specify both --all and --clients");
tm().transact(
() -> {
Optional<DomainBase> domainOpt =
@@ -69,27 +88,39 @@ class EnqueuePollMessageCommand extends MutatingCommand {
checkArgument(
domainOpt.isPresent(), "Domain %s doesn't exist or isn't active", domainName);
DomainBase domain = domainOpt.get();
String registrarId =
Optional.ofNullable(clientId).orElse(domain.getCurrentSponsorRegistrarId());
ImmutableList<String> registrarIds;
if (sendToAll) {
registrarIds =
Streams.stream(Registrar.loadAllCached())
.filter(r -> r.isLive() && r.getType() == Registrar.Type.REAL)
.map(Registrar::getRegistrarId)
.collect(ImmutableList.toImmutableList());
} else if (!isNullOrEmpty(clientIds)) {
registrarIds = ImmutableList.copyOf(clientIds);
} else {
registrarIds = ImmutableList.of(domain.getCurrentSponsorRegistrarId());
}
HistoryEntry historyEntry =
new DomainHistory.Builder()
.setDomain(domain)
.setType(SYNTHETIC)
.setBySuperuser(true)
.setReason("Manual enqueueing of poll message")
.setReason("Manual enqueueing of poll message: " + message)
.setModificationTime(tm().getTransactionTime())
.setRequestedByRegistrar(false)
.setRegistrarId(registrarId)
.build();
PollMessage.OneTime pollMessage =
new PollMessage.OneTime.Builder()
.setRegistrarId(registrarId)
.setParent(historyEntry)
.setEventTime(tm().getTransactionTime())
.setMsg(message)
.setRegistrarId(registryAdminClientId)
.build();
stageEntityChange(null, historyEntry);
stageEntityChange(null, pollMessage);
for (String registrarId : registrarIds) {
stageEntityChange(
null,
new PollMessage.OneTime.Builder()
.setRegistrarId(registrarId)
.setParent(historyEntry)
.setEventTime(tm().getTransactionTime())
.setMsg(message)
.build());
}
});
}
}

View File

@@ -16,7 +16,9 @@ package google.registry.tools;
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
import static google.registry.model.tld.Registries.assertTldsExist;
import static google.registry.rde.RdeModule.PARAM_BEAM;
import static google.registry.rde.RdeModule.PARAM_DIRECTORY;
import static google.registry.rde.RdeModule.PARAM_LENIENT;
import static google.registry.rde.RdeModule.PARAM_MANUAL;
import static google.registry.rde.RdeModule.PARAM_MODE;
import static google.registry.rde.RdeModule.PARAM_REVISION;
@@ -65,6 +67,20 @@ final class GenerateEscrowDepositCommand implements CommandWithRemoteApi {
description = "Mode of operation: FULL for RDE deposits, THIN for BRDA deposits.")
private RdeMode mode = RdeMode.FULL;
@Parameter(
names = {"-l", "--lenient"},
description =
"Whether to run RDE in LENIENT mode, which omits validation of the generated "
+ "XML deposit files.")
private boolean lenient = false;
@Parameter(
names = {"-b", "--beam"},
description =
"Whether to explicitly launch the beam pipeline instead of letting the action decide"
+ " which one to run.")
private boolean beam = false;
@Parameter(
names = {"-r", "--revision"},
description = "Revision number. Use >0 for resends.")
@@ -119,6 +135,8 @@ final class GenerateEscrowDepositCommand implements CommandWithRemoteApi {
.param(PARAM_MANUAL, String.valueOf(true))
.param(PARAM_MODE, mode.toString())
.param(PARAM_DIRECTORY, outdir)
.param(PARAM_LENIENT, Boolean.toString(lenient))
.param(PARAM_BEAM, Boolean.toString(beam))
.param(PARAM_TLDS, tlds.stream().collect(Collectors.joining(",")))
.param(
PARAM_WATERMARKS,

View File

@@ -48,6 +48,7 @@ import com.google.common.truth.Truth8;
import com.google.common.util.concurrent.MoreExecutors;
import com.googlecode.objectify.Key;
import google.registry.gcs.GcsUtils;
import google.registry.model.common.ClassPathManager;
import google.registry.model.common.DatabaseMigrationStateSchedule;
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
import google.registry.model.contact.ContactResource;
@@ -67,7 +68,6 @@ import google.registry.model.replay.SqlReplayCheckpoint;
import google.registry.model.server.Lock;
import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumList.PremiumEntry;
import google.registry.model.translators.VKeyTranslatorFactory;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.persistence.transaction.TransactionManagerFactory;
@@ -130,7 +130,7 @@ public class ReplayCommitLogsToSqlActionTest {
@BeforeAll
static void beforeAll() {
VKeyTranslatorFactory.addTestEntityClass(TestObject.class);
ClassPathManager.addTestEntityClass(TestObject.class);
}
@BeforeEach

View File

@@ -344,7 +344,7 @@ public class SyncRegistrarsSheetTest {
ImmutableMap<String, String> row = getOnlyElement(getOnlyElement(rowsCaptor.getAllValues()));
assertThat(row).containsEntry("clientIdentifier", "SomeRegistrar");
assertThat(row).containsEntry("registrarName", "Some Registrar");
assertThat(row).containsEntry("state", "");
assertThat(row).containsEntry("state", "ACTIVE");
assertThat(row).containsEntry("ianaIdentifier", "8");
assertThat(row).containsEntry("billingIdentifier", "");
assertThat(row).containsEntry("primaryContacts", "");

View File

@@ -0,0 +1,177 @@
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.model.common;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.model.billing.BillingEvent.Cancellation;
import google.registry.model.billing.BillingEvent.Modification;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.billing.BillingEvent.Recurring;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainBase;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.host.HostResource;
import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.EppResourceIndexBucket;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyContactIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex;
import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
import google.registry.model.ofy.CommitLogBucket;
import google.registry.model.ofy.CommitLogCheckpoint;
import google.registry.model.ofy.CommitLogCheckpointRoot;
import google.registry.model.ofy.CommitLogManifest;
import google.registry.model.ofy.CommitLogMutation;
import google.registry.model.poll.PollMessage;
import google.registry.model.rde.RdeRevision;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.replay.LastSqlTransaction;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.server.Lock;
import google.registry.model.server.ServerSecret;
import google.registry.model.tld.Registry;
import google.registry.testing.TestObject;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link ClassPathManager}. */
public class ClassPathManagerTest {
@Test
void getClass_classInClassRegistry_returnsClass() throws ClassNotFoundException {
/**
* Class names are used in stringified vkeys, which can be present in task queues. Class name is
* required to create a vkey. Changing these names could break task queue entries that are
* present during a rollout. If you want to change the names of any of the classses supported in
* CLASS_REGISTRY, you'll need to introduce some mechanism to deal with this. One way is to find
* the corresponding class name by calling ClassPathManager.getClassName(clazz). The classes
* below are all classes supported in CLASS_REGISTRY. This test breaks if someone changes a
* classname without preserving the original name.
*/
assertThat(ClassPathManager.getClass("ForeignKeyContactIndex"))
.isEqualTo(ForeignKeyContactIndex.class);
assertThat(ClassPathManager.getClass("Modification")).isEqualTo(Modification.class);
assertThat(ClassPathManager.getClass("CommitLogCheckpoint"))
.isEqualTo(CommitLogCheckpoint.class);
assertThat(ClassPathManager.getClass("CommitLogManifest")).isEqualTo(CommitLogManifest.class);
assertThat(ClassPathManager.getClass("AllocationToken")).isEqualTo(AllocationToken.class);
assertThat(ClassPathManager.getClass("OneTime")).isEqualTo(OneTime.class);
assertThat(ClassPathManager.getClass("Cursor")).isEqualTo(Cursor.class);
assertThat(ClassPathManager.getClass("RdeRevision")).isEqualTo(RdeRevision.class);
assertThat(ClassPathManager.getClass("HostResource")).isEqualTo(HostResource.class);
assertThat(ClassPathManager.getClass("Recurring")).isEqualTo(Recurring.class);
assertThat(ClassPathManager.getClass("Registrar")).isEqualTo(Registrar.class);
assertThat(ClassPathManager.getClass("ContactResource")).isEqualTo(ContactResource.class);
assertThat(ClassPathManager.getClass("Cancellation")).isEqualTo(Cancellation.class);
assertThat(ClassPathManager.getClass("RegistrarContact")).isEqualTo(RegistrarContact.class);
assertThat(ClassPathManager.getClass("CommitLogBucket")).isEqualTo(CommitLogBucket.class);
assertThat(ClassPathManager.getClass("LastSqlTransaction")).isEqualTo(LastSqlTransaction.class);
assertThat(ClassPathManager.getClass("CommitLogCheckpointRoot"))
.isEqualTo(CommitLogCheckpointRoot.class);
assertThat(ClassPathManager.getClass("GaeUserIdConverter")).isEqualTo(GaeUserIdConverter.class);
assertThat(ClassPathManager.getClass("EppResourceIndexBucket"))
.isEqualTo(EppResourceIndexBucket.class);
assertThat(ClassPathManager.getClass("Registry")).isEqualTo(Registry.class);
assertThat(ClassPathManager.getClass("EntityGroupRoot")).isEqualTo(EntityGroupRoot.class);
assertThat(ClassPathManager.getClass("Lock")).isEqualTo(Lock.class);
assertThat(ClassPathManager.getClass("DomainBase")).isEqualTo(DomainBase.class);
assertThat(ClassPathManager.getClass("CommitLogMutation")).isEqualTo(CommitLogMutation.class);
assertThat(ClassPathManager.getClass("HistoryEntry")).isEqualTo(HistoryEntry.class);
assertThat(ClassPathManager.getClass("PollMessage")).isEqualTo(PollMessage.class);
assertThat(ClassPathManager.getClass("ForeignKeyHostIndex"))
.isEqualTo(ForeignKeyHostIndex.class);
assertThat(ClassPathManager.getClass("ServerSecret")).isEqualTo(ServerSecret.class);
assertThat(ClassPathManager.getClass("EppResourceIndex")).isEqualTo(EppResourceIndex.class);
assertThat(ClassPathManager.getClass("ForeignKeyDomainIndex"))
.isEqualTo(ForeignKeyDomainIndex.class);
}
@Test
void getClass_classNotInClassRegistry_throwsException() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class, () -> ClassPathManager.getClass("DomainHistory"));
assertThat(thrown).hasMessageThat().contains("Class not found in class registry");
}
@Test
void getClassName_classNotInClassRegistry_throwsException() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> ClassPathManager.getClassName(DomainHistory.class));
assertThat(thrown).hasMessageThat().contains("Class not found in class name registry");
}
@Test
void getClassName() {
/**
* Class names are used in stringified vkeys, which can be present in task queues. Class name is
* required to create a vkey. Changing these names could break task queue entries that are
* present during a rollout. If you want to change the names of any of the classses supported in
* CLASS_NAME_REGISTRY, you'll need to introduce some mechanism to deal with this.
* ClassPathManager.getClassName(clazz) allows you to verify the corresponding name of a class.
* The classes below are all classes supported in CLASS_NAME_REGISTRY. This test breaks if
* someone changes a classname without preserving the original name.
*/
assertThat(ClassPathManager.getClassName(ForeignKeyContactIndex.class))
.isEqualTo("ForeignKeyContactIndex");
assertThat(ClassPathManager.getClassName(Modification.class)).isEqualTo("Modification");
assertThat(ClassPathManager.getClassName(CommitLogCheckpoint.class))
.isEqualTo("CommitLogCheckpoint");
assertThat(ClassPathManager.getClassName(CommitLogManifest.class))
.isEqualTo("CommitLogManifest");
assertThat(ClassPathManager.getClassName(AllocationToken.class)).isEqualTo("AllocationToken");
assertThat(ClassPathManager.getClassName(OneTime.class)).isEqualTo("OneTime");
assertThat(ClassPathManager.getClassName(Cursor.class)).isEqualTo("Cursor");
assertThat(ClassPathManager.getClassName(RdeRevision.class)).isEqualTo("RdeRevision");
assertThat(ClassPathManager.getClassName(HostResource.class)).isEqualTo("HostResource");
assertThat(ClassPathManager.getClassName(Recurring.class)).isEqualTo("Recurring");
assertThat(ClassPathManager.getClassName(Registrar.class)).isEqualTo("Registrar");
assertThat(ClassPathManager.getClassName(ContactResource.class)).isEqualTo("ContactResource");
assertThat(ClassPathManager.getClassName(Cancellation.class)).isEqualTo("Cancellation");
assertThat(ClassPathManager.getClassName(RegistrarContact.class)).isEqualTo("RegistrarContact");
assertThat(ClassPathManager.getClassName(CommitLogBucket.class)).isEqualTo("CommitLogBucket");
assertThat(ClassPathManager.getClassName(LastSqlTransaction.class))
.isEqualTo("LastSqlTransaction");
assertThat(ClassPathManager.getClassName(CommitLogCheckpointRoot.class))
.isEqualTo("CommitLogCheckpointRoot");
assertThat(ClassPathManager.getClassName(GaeUserIdConverter.class))
.isEqualTo("GaeUserIdConverter");
assertThat(ClassPathManager.getClassName(EppResourceIndexBucket.class))
.isEqualTo("EppResourceIndexBucket");
assertThat(ClassPathManager.getClassName(Registry.class)).isEqualTo("Registry");
assertThat(ClassPathManager.getClassName(EntityGroupRoot.class)).isEqualTo("EntityGroupRoot");
assertThat(ClassPathManager.getClassName(Lock.class)).isEqualTo("Lock");
assertThat(ClassPathManager.getClassName(DomainBase.class)).isEqualTo("DomainBase");
assertThat(ClassPathManager.getClassName(CommitLogMutation.class))
.isEqualTo("CommitLogMutation");
assertThat(ClassPathManager.getClassName(HistoryEntry.class)).isEqualTo("HistoryEntry");
assertThat(ClassPathManager.getClassName(PollMessage.class)).isEqualTo("PollMessage");
assertThat(ClassPathManager.getClassName(ForeignKeyHostIndex.class))
.isEqualTo("ForeignKeyHostIndex");
assertThat(ClassPathManager.getClassName(ServerSecret.class)).isEqualTo("ServerSecret");
assertThat(ClassPathManager.getClassName(EppResourceIndex.class)).isEqualTo("EppResourceIndex");
assertThat(ClassPathManager.getClassName(ForeignKeyDomainIndex.class))
.isEqualTo("ForeignKeyDomainIndex");
}
@Test
void addTestEntityClass_success() {
ClassPathManager.addTestEntityClass(TestObject.class);
assertThat(ClassPathManager.getClass("TestObject")).isEqualTo(TestObject.class);
}
}

View File

@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import com.googlecode.objectify.Key;
import google.registry.model.billing.BillingEvent;
import google.registry.model.common.ClassPathManager;
import google.registry.model.domain.DomainBase;
import google.registry.model.ofy.CommitLogCheckpoint;
import google.registry.model.ofy.CommitLogCheckpointRoot;
@@ -32,6 +33,7 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
/** Unit tests for {@link VKeyTranslatorFactory}. */
public class VKeyTranslatorFactoryTest {
@RegisterExtension
@@ -45,7 +47,7 @@ public class VKeyTranslatorFactoryTest {
@BeforeAll
static void beforeAll() {
VKeyTranslatorFactory.addTestEntityClass(TestObject.class);
ClassPathManager.addTestEntityClass(TestObject.class);
}
@Test

View File

@@ -27,9 +27,9 @@ import com.google.common.collect.ImmutableMap;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import google.registry.model.billing.BillingEvent.OneTime;
import google.registry.model.common.ClassPathManager;
import google.registry.model.domain.DomainBase;
import google.registry.model.registrar.RegistrarContact;
import google.registry.model.translators.VKeyTranslatorFactory;
import google.registry.testing.AppEngineExtension;
import google.registry.testing.TaskQueueHelper.TaskMatcher;
import google.registry.testing.TestObject;
@@ -63,7 +63,7 @@ class VKeyTest {
@BeforeAll
static void beforeAll() {
VKeyTranslatorFactory.addTestEntityClass(TestObject.class);
ClassPathManager.addTestEntityClass(TestObject.class);
}
@Test
@@ -161,14 +161,13 @@ class VKeyTest {
@Test
void testStringify_sqlOnlyVKey() throws Exception {
assertThat(VKey.createSql(TestObject.class, "foo").stringify())
.isEqualTo("kind:google.registry.testing.TestObject@sql:rO0ABXQAA2Zvbw");
.isEqualTo("kind:TestObject@sql:rO0ABXQAA2Zvbw");
}
@Test
void testStringify_ofyOnlyVKey() throws Exception {
assertThat(VKey.createOfy(TestObject.class, Key.create(TestObject.class, "foo")).stringify())
.isEqualTo(
"kind:google.registry.testing.TestObject@ofy:agR0ZXN0chMLEgpUZXN0T2JqZWN0IgNmb28M");
.isEqualTo("kind:TestObject@ofy:agR0ZXN0chMLEgpUZXN0T2JqZWN0IgNmb28M");
}
@Test
@@ -178,8 +177,8 @@ class VKeyTest {
VKey<DomainBase> vkey = VKey.fromWebsafeKey(key.getString());
assertThat(vkey.stringify())
.isEqualTo(
"kind:google.registry.model.domain.DomainBas"
+ "e@sql:rO0ABXQABlJPSUQtMQ"
"kind:DomainBase"
+ "@sql:rO0ABXQABlJPSUQtMQ"
+ "@ofy:agR0ZXN0chYLEgpEb21haW5CYXNlIgZST0lELTEM");
}
@@ -188,7 +187,7 @@ class VKeyTest {
assertThat(
VKey.create(TestObject.class, "foo", Key.create(TestObject.create("foo"))).stringify())
.isEqualTo(
"kind:google.registry.testing.TestObject@sql:rO0ABXQAA2Zvbw@ofy:agR0ZXN0cjELEg9FbnRpdH"
"kind:TestObject@sql:rO0ABXQAA2Zvbw@ofy:agR0ZXN0cjELEg9FbnRpdH"
+ "lHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M");
}
@@ -197,22 +196,20 @@ class VKeyTest {
assertThat(
VKey.create(TestObject.class, "test", Key.create(TestObject.create("foo"))).stringify())
.isEqualTo(
"kind:google.registry.testing.TestObject@sql:rO0ABXQABHRlc3Q@ofy:agR0ZXN0cjELEg9FbnRpd"
"kind:TestObject@sql:rO0ABXQABHRlc3Q@ofy:agR0ZXN0cjELEg9FbnRpd"
+ "HlHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M");
}
/** Test create() via different vkey string representations. */
@Test
void testCreate_stringifedVKey_sqlOnlyVKeyString() throws Exception {
assertThat(VKey.create("kind:google.registry.testing.TestObject@sql:rO0ABXQAA2Zvbw"))
assertThat(VKey.create("kind:TestObject@sql:rO0ABXQAA2Zvbw"))
.isEqualTo(VKey.createSql(TestObject.class, "foo"));
}
@Test
void testCreate_stringifedVKey_ofyOnlyVKeyString() throws Exception {
assertThat(
VKey.create(
"kind:google.registry.testing.TestObject@ofy:agR0ZXN0chMLEgpUZXN0T2JqZWN0IgNmb28M"))
assertThat(VKey.create("kind:TestObject@ofy:agR0ZXN0chMLEgpUZXN0T2JqZWN0IgNmb28M"))
.isEqualTo(VKey.createOfy(TestObject.class, Key.create(TestObject.class, "foo")));
}
@@ -220,7 +217,7 @@ class VKeyTest {
void testCreate_stringifedVKey_asymmetricVKeyString() throws Exception {
assertThat(
VKey.create(
"kind:google.registry.testing.TestObject@sql:rO0ABXQABHRlc3Q@ofy:agR0ZXN0cjELEg9Fb"
"kind:TestObject@sql:rO0ABXQABHRlc3Q@ofy:agR0ZXN0cjELEg9Fb"
+ "nRpdHlHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M"))
.isEqualTo(VKey.create(TestObject.class, "test", Key.create(TestObject.create("foo"))));
}
@@ -229,7 +226,7 @@ class VKeyTest {
void testCreate_stringifedVKey_sqlAndOfyVKeyString() throws Exception {
assertThat(
VKey.create(
"kind:google.registry.testing.TestObject@sql:rO0ABXQAA2Zvbw@ofy:agR0ZXN0cjELEg9Fbn"
"kind:TestObject@sql:rO0ABXQAA2Zvbw@ofy:agR0ZXN0cjELEg9Fbn"
+ "RpdHlHcm91cFJvb3QiCWNyb3NzLXRsZAwLEgpUZXN0T2JqZWN0IgNmb28M"))
.isEqualTo(VKey.create(TestObject.class, "foo", Key.create(TestObject.create("foo"))));
}
@@ -238,7 +235,7 @@ class VKeyTest {
void testCreate_stringifyVkey_fromWebsafeKey() throws Exception {
assertThat(
VKey.create(
"kind:google.registry.model.domain.DomainBase@sql:rO0ABXQABlJPSUQtMQ"
"kind:DomainBase@sql:rO0ABXQABlJPSUQtMQ"
+ "@ofy:agR0ZXN0chYLEgpEb21haW5CYXNlIgZST0lELTEM"))
.isEqualTo(
VKey.fromWebsafeKey(
@@ -257,11 +254,10 @@ class VKeyTest {
void testCreate_invalidStringifiedVKey_failure() throws Exception {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> VKey.create("kind:google.registry.testing.TestObject@sq:l@ofya:bc"));
IllegalArgumentException.class, () -> VKey.create("kind:TestObject@sq:l@ofya:bc"));
assertThat(thrown)
.hasMessageThat()
.contains("Cannot parse key string: kind:google.registry.testing.TestObject@sq:l@ofya:bc");
.contains("Cannot parse key string: kind:TestObject@sq:l@ofya:bc");
}
@Test

View File

@@ -96,6 +96,7 @@ import google.registry.model.index.ForeignKeyIndex;
import google.registry.model.poll.PollMessage;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntryDao;
@@ -758,6 +759,7 @@ public class DatabaseHelper {
.setRegistrarId(registrarId)
.setRegistrarName(registrarName)
.setType(type)
.setState(State.ACTIVE)
.setIanaIdentifier(ianaIdentifier)
.setLocalizedAddress(
new RegistrarAddress.Builder()

View File

@@ -20,6 +20,7 @@ import static google.registry.testing.DatabaseHelper.assertPollMessages;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
import static google.registry.testing.HistoryEntrySubject.assertAboutHistoryEntries;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -47,6 +48,8 @@ class EnqueuePollMessageCommandTest extends CommandTestCase<EnqueuePollMessageCo
createTld("tld");
inject.setStaticField(Ofy.class, "clock", fakeClock);
domain = persistActiveDomain("example.tld");
persistNewRegistrar("AdminRegistrar");
command.registryAdminClientId = "AdminRegistrar";
fakeClock.advanceOneMilli();
}
@@ -59,11 +62,11 @@ class EnqueuePollMessageCommandTest extends CommandTestCase<EnqueuePollMessageCo
.that(synthetic)
.bySuperuser(true)
.and()
.hasMetadataReason("Manual enqueueing of poll message")
.hasMetadataReason("Manual enqueueing of poll message: This domain is bad")
.and()
.hasNoXml()
.and()
.hasRegistrarId("TheRegistrar")
.hasRegistrarId("AdminRegistrar")
.and()
.hasModificationTime(fakeClock.nowUtc())
.and()
@@ -79,24 +82,35 @@ class EnqueuePollMessageCommandTest extends CommandTestCase<EnqueuePollMessageCo
}
@TestOfyAndSql
void testSuccess_specifyClientId() throws Exception {
void testSuccess_specifyClientIds() throws Exception {
persistNewRegistrar("foobaz");
runCommandForced(
"--domain=example.tld", "--message=This domain needs work", "--client=NewRegistrar");
"--domain=example.tld",
"--message=This domain needs work",
"--clients=TheRegistrar,NewRegistrar,foobaz");
HistoryEntry synthetic = getOnlyHistoryEntryOfType(domain, SYNTHETIC);
assertAboutHistoryEntries()
.that(synthetic)
.bySuperuser(true)
.and()
.hasMetadataReason("Manual enqueueing of poll message")
.hasMetadataReason("Manual enqueueing of poll message: This domain needs work")
.and()
.hasNoXml()
.and()
.hasRegistrarId("NewRegistrar")
.hasRegistrarId("AdminRegistrar")
.and()
.hasModificationTime(fakeClock.nowUtc())
.and()
.hasMetadataRequestedByRegistrar(false);
assertPollMessages(
"TheRegistrar",
new PollMessage.OneTime.Builder()
.setParent(synthetic)
.setMsg("This domain needs work")
.setRegistrarId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.build());
assertPollMessages(
"NewRegistrar",
new PollMessage.OneTime.Builder()
@@ -105,6 +119,59 @@ class EnqueuePollMessageCommandTest extends CommandTestCase<EnqueuePollMessageCo
.setRegistrarId("NewRegistrar")
.setEventTime(fakeClock.nowUtc())
.build());
assertPollMessages(
"foobaz",
new PollMessage.OneTime.Builder()
.setParent(synthetic)
.setMsg("This domain needs work")
.setRegistrarId("foobaz")
.setEventTime(fakeClock.nowUtc())
.build());
}
@TestOfyAndSql
void testSuccess_sendToAllRegistrars() throws Exception {
persistNewRegistrar("foobaz");
runCommandForced("--domain=example.tld", "--message=This domain needs work", "--all=true");
HistoryEntry synthetic = getOnlyHistoryEntryOfType(domain, SYNTHETIC);
assertAboutHistoryEntries()
.that(synthetic)
.bySuperuser(true)
.and()
.hasMetadataReason("Manual enqueueing of poll message: This domain needs work")
.and()
.hasNoXml()
.and()
.hasRegistrarId("AdminRegistrar")
.and()
.hasModificationTime(fakeClock.nowUtc())
.and()
.hasMetadataRequestedByRegistrar(false);
assertPollMessages(
"TheRegistrar",
new PollMessage.OneTime.Builder()
.setParent(synthetic)
.setMsg("This domain needs work")
.setRegistrarId("TheRegistrar")
.setEventTime(fakeClock.nowUtc())
.build());
assertPollMessages(
"NewRegistrar",
new PollMessage.OneTime.Builder()
.setParent(synthetic)
.setMsg("This domain needs work")
.setRegistrarId("NewRegistrar")
.setEventTime(fakeClock.nowUtc())
.build());
assertPollMessages(
"foobaz",
new PollMessage.OneTime.Builder()
.setParent(synthetic)
.setMsg("This domain needs work")
.setRegistrarId("foobaz")
.setEventTime(fakeClock.nowUtc())
.build());
}
@TestOfyAndSql
@@ -131,4 +198,18 @@ class EnqueuePollMessageCommandTest extends CommandTestCase<EnqueuePollMessageCo
assertThrows(ParameterException.class, () -> runCommandForced("--domain=example.tld"));
assertThat(thrown).hasMessageThat().contains("The following option is required: -m, --message");
}
@TestOfyAndSql
void testCantSpecifyClientIdsAndAll() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--domain=example.tld",
"--message=Domain is ended",
"--all=true",
"--clients=TheRegistrar"));
assertThat(thrown).hasMessageThat().isEqualTo("Cannot specify both --all and --clients");
}
}

View File

@@ -188,7 +188,55 @@ public class GenerateEscrowDepositCommandTest
}
@Test
void testCommand_success() throws Exception {
void testCommand_successWithLenientValidationMode() throws Exception {
runCommand(
"--tld=tld",
"--watermark=2017-01-01T00:00:00Z",
"--mode=thin",
"--lenient",
"-r 42",
"-o test");
assertTasksEnqueued(
"rde-report",
new TaskMatcher()
.url("/_dr/task/rdeStaging")
.header("Host", "backend.test.localhost")
.param("mode", "THIN")
.param("lenient", "true")
.param("watermarks", "2017-01-01T00:00:00.000Z")
.param("tlds", "tld")
.param("directory", "test")
.param("manual", "true")
.param("revision", "42"));
}
@Test
void testCommand_successWithBeam() throws Exception {
runCommand(
"--tld=tld",
"--watermark=2017-01-01T00:00:00Z",
"--mode=thin",
"--beam",
"-r 42",
"-o test");
assertTasksEnqueued(
"rde-report",
new TaskMatcher()
.url("/_dr/task/rdeStaging")
.header("Host", "backend.test.localhost")
.param("mode", "THIN")
.param("beam", "true")
.param("watermarks", "2017-01-01T00:00:00.000Z")
.param("tlds", "tld")
.param("directory", "test")
.param("manual", "true")
.param("revision", "42"));
}
@Test
void testCommand_successWithDefaultValidationMode() throws Exception {
runCommand("--tld=tld", "--watermark=2017-01-01T00:00:00Z", "--mode=thin", "-r 42", "-o test");
assertTasksEnqueued(
@@ -197,6 +245,7 @@ public class GenerateEscrowDepositCommandTest
.url("/_dr/task/rdeStaging")
.header("Host", "backend.test.localhost")
.param("mode", "THIN")
.param("lenient", "false")
.param("watermarks", "2017-01-01T00:00:00.000Z")
.param("tlds", "tld")
.param("directory", "test")
@@ -213,6 +262,8 @@ public class GenerateEscrowDepositCommandTest
new TaskMatcher()
.url("/_dr/task/rdeStaging")
.header("Host", "backend.test.localhost")
.param("lenient", "false")
.param("beam", "false")
.param("mode", "THIN")
.param("watermarks", "2017-01-01T00:00:00.000Z")
.param("tlds", "tld")
@@ -230,6 +281,8 @@ public class GenerateEscrowDepositCommandTest
.url("/_dr/task/rdeStaging")
.header("Host", "backend.test.localhost")
.param("mode", "FULL")
.param("lenient", "false")
.param("beam", "false")
.param("watermarks", "2017-01-01T00:00:00.000Z")
.param("tlds", "tld")
.param("directory", "test")
@@ -252,6 +305,8 @@ public class GenerateEscrowDepositCommandTest
.url("/_dr/task/rdeStaging")
.header("Host", "backend.test.localhost")
.param("mode", "THIN")
.param("lenient", "false")
.param("beam", "false")
.param("watermarks", "2017-01-01T00:00:00.000Z,2017-01-02T00:00:00.000Z")
.param("tlds", "tld,anothertld")
.param("directory", "test")

View File

@@ -8,7 +8,8 @@ com.diffplug.gradle.spotless:com.diffplug.gradle.spotless.gradle.plugin:3.25.0
com.diffplug.spotless:spotless-lib-extra:1.25.0
com.diffplug.spotless:spotless-lib:1.25.0
com.diffplug.spotless:spotless-plugin-gradle:3.25.0
com.dorongold.task-tree:com.dorongold.task-tree.gradle.plugin:1.5
com.dorongold.plugins:task-tree:2.1.0
com.dorongold.task-tree:com.dorongold.task-tree.gradle.plugin:2.1.0
com.github.jengelman.gradle.plugins:shadow:5.1.0
com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:5.1.0
com.github.node-gradle.node:com.github.node-gradle.node.gradle.plugin:3.0.1
@@ -30,7 +31,6 @@ com.netflix.nebula:gradle-lint-plugin:16.0.2
com.netflix.nebula:nebula-gradle-interop:1.0.11
commons-io:commons-io:2.6
commons-lang:commons-lang:2.6
gradle.plugin.com.dorongold.plugins:task-tree:1.5
javax.inject:javax.inject:1
junit:junit:4.12
nebula.lint:nebula.lint.gradle.plugin:16.0.2