mirror of
https://github.com/google/nomulus
synced 2026-05-26 09:41:09 +00:00
Compare commits
46 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34d329c158 | ||
|
|
425ecdcd87 | ||
|
|
77ee124374 | ||
|
|
b9742adc0b | ||
|
|
d4cd25c4ae | ||
|
|
8b7e938ed6 | ||
|
|
c216c874b4 | ||
|
|
0ab9471c8d | ||
|
|
d482754f66 | ||
|
|
fe086b43f5 | ||
|
|
95f1bca3fb | ||
|
|
178a2323d9 | ||
|
|
a44aa1378f | ||
|
|
d0f625f70e | ||
|
|
fb59874234 | ||
|
|
b6083e227f | ||
|
|
5805b6859e | ||
|
|
3108e8a871 | ||
|
|
ec142caf9c | ||
|
|
e60ad58098 | ||
|
|
83e9e7fb5c | ||
|
|
438c523fcb | ||
|
|
025a2faff2 | ||
|
|
fd822dd333 | ||
|
|
9b93749d43 | ||
|
|
71a8579ece | ||
|
|
cda51f13dc | ||
|
|
1de5b5dcc1 | ||
|
|
32279e42e4 | ||
|
|
ba0f90bdaf | ||
|
|
85308eb975 | ||
|
|
ed62f27a4a | ||
|
|
75851399ba | ||
|
|
6d54c8d113 | ||
|
|
34dfa2760e | ||
|
|
ff39a4a763 | ||
|
|
b1cd8c5a6f | ||
|
|
28c7bc3085 | ||
|
|
f36d22f4b1 | ||
|
|
ef3ce79b8a | ||
|
|
85317e3982 | ||
|
|
a53b71ecd5 | ||
|
|
fc9446876f | ||
|
|
654b165dff | ||
|
|
14d68d4cb2 | ||
|
|
bbf405d566 |
@@ -103,6 +103,7 @@ explodeWar.doLast {
|
||||
file("${it.explodedAppDirectory}/WEB-INF/lib/tools.jar").setWritable(true)
|
||||
}
|
||||
|
||||
appengineDeployAll.finalizedBy ':cloudSchedulerDeployer'
|
||||
rootProject.deploy.dependsOn appengineDeployAll
|
||||
rootProject.stage.dependsOn appengineStage
|
||||
tasks['war'].dependsOn ':console-webapp:buildConsoleWebappProd'
|
||||
|
||||
15
build.gradle
15
build.gradle
@@ -558,6 +558,21 @@ task coreDev {
|
||||
|
||||
javadocDependentTasks.each { tasks.javadoc.dependsOn(it) }
|
||||
|
||||
// Runs the script, which deploys cloud scheduler tasks based on the config
|
||||
task cloudSchedulerDeployer {
|
||||
doLast {
|
||||
def env = environment
|
||||
if (!prodOrSandboxEnv) {
|
||||
exec {
|
||||
commandLine 'go', 'run',
|
||||
"${rootDir}/release/builder/cloudSchedulerDeployer.go",
|
||||
"${rootDir}/core/src/main/java/google/registry/env/${env}/default/WEB-INF/cloud-scheduler-tasks.xml",
|
||||
"domain-registry-${env}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// disable javadoc in subprojects, these will break because they don't have
|
||||
// the correct classpath (see above).
|
||||
gradle.taskGraph.whenReady { graph ->
|
||||
|
||||
@@ -270,6 +270,10 @@
|
||||
"moduleLicense": "Public Domain",
|
||||
"moduleName": "org.tukaani:xz"
|
||||
},
|
||||
{
|
||||
"moduleLicense": "Public Domain",
|
||||
"moduleName": "org.json:json"
|
||||
},
|
||||
{
|
||||
// "Apache License, Version 2.0".
|
||||
"moduleLicense": null,
|
||||
|
||||
3890
console-webapp/package-lock.json
generated
3890
console-webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,24 +13,24 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^15.1.0",
|
||||
"@angular/cdk": "^15.0.4",
|
||||
"@angular/common": "^15.1.0",
|
||||
"@angular/compiler": "^15.1.0",
|
||||
"@angular/core": "^15.1.0",
|
||||
"@angular/forms": "^15.1.0",
|
||||
"@angular/material": "^15.0.4",
|
||||
"@angular/platform-browser": "^15.1.0",
|
||||
"@angular/platform-browser-dynamic": "^15.1.0",
|
||||
"@angular/router": "^15.1.0",
|
||||
"@angular/animations": "^15.2.2",
|
||||
"@angular/cdk": "^15.2.2",
|
||||
"@angular/common": "^15.2.2",
|
||||
"@angular/compiler": "^15.2.2",
|
||||
"@angular/core": "^15.2.2",
|
||||
"@angular/forms": "^15.2.2",
|
||||
"@angular/material": "^15.2.2",
|
||||
"@angular/platform-browser": "^15.2.2",
|
||||
"@angular/platform-browser-dynamic": "^15.2.2",
|
||||
"@angular/router": "^15.2.2",
|
||||
"rxjs": "~7.5.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^15.1.0",
|
||||
"@angular/cli": "~15.1.0",
|
||||
"@angular/compiler-cli": "^15.1.0",
|
||||
"@angular-devkit/build-angular": "^15.2.4",
|
||||
"@angular/cli": "~15.2.4",
|
||||
"@angular/compiler-cli": "^15.2.2",
|
||||
"@types/jasmine": "~4.0.0",
|
||||
"@types/node": "^18.11.18",
|
||||
"concurrently": "^7.6.0",
|
||||
|
||||
@@ -1028,6 +1028,7 @@ test {
|
||||
// TODO(weiminyu): Remove dependency on sqlIntegrationTest
|
||||
}.dependsOn(fragileTest, outcastTest, standardTest, registryToolIntegrationTest, sqlIntegrationTest)
|
||||
|
||||
|
||||
// When we override tests, we also break the cleanTest command.
|
||||
cleanTest.dependsOn(cleanFragileTest, cleanOutcastTest, cleanStandardTest,
|
||||
cleanRegistryToolIntegrationTest, cleanSqlIntegrationTest)
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
@@ -86,6 +85,6 @@ public final class AsyncTaskEnqueuer {
|
||||
cloudTasksUtils.enqueue(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
ResaveEntityAction.PATH, Service.BACKEND.toString(), params, etaDuration));
|
||||
ResaveEntityAction.PATH, Service.BACKEND, params, etaDuration));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.util;
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
@@ -36,12 +36,18 @@ import com.google.common.net.MediaType;
|
||||
import com.google.common.net.UrlEscapers;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.util.Timestamps;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Utilities for dealing with Cloud Tasks. */
|
||||
@@ -57,11 +63,12 @@ public class CloudTasksUtils implements Serializable {
|
||||
private final String locationId;
|
||||
private final SerializableCloudTasksClient client;
|
||||
|
||||
@Inject
|
||||
public CloudTasksUtils(
|
||||
Retrier retrier,
|
||||
Clock clock,
|
||||
String projectId,
|
||||
String locationId,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("locationId") String locationId,
|
||||
SerializableCloudTasksClient client) {
|
||||
this.retrier = retrier;
|
||||
this.clock = clock;
|
||||
@@ -108,7 +115,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
* the worker service</a>
|
||||
*/
|
||||
private Task createTask(
|
||||
String path, HttpMethod method, String service, Multimap<String, String> params) {
|
||||
String path, HttpMethod method, Service service, Multimap<String, String> params) {
|
||||
checkArgument(
|
||||
path != null && !path.isEmpty() && path.charAt(0) == '/',
|
||||
"The path must start with a '/'.");
|
||||
@@ -119,7 +126,8 @@ public class CloudTasksUtils implements Serializable {
|
||||
AppEngineHttpRequest.Builder requestBuilder =
|
||||
AppEngineHttpRequest.newBuilder()
|
||||
.setHttpMethod(method)
|
||||
.setAppEngineRouting(AppEngineRouting.newBuilder().setService(service).build());
|
||||
.setAppEngineRouting(
|
||||
AppEngineRouting.newBuilder().setService(service.toString()).build());
|
||||
|
||||
if (!CollectionUtils.isNullOrEmpty(params)) {
|
||||
Escaper escaper = UrlEscapers.urlPathSegmentEscaper();
|
||||
@@ -165,7 +173,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
private Task createTaskWithJitter(
|
||||
String path,
|
||||
HttpMethod method,
|
||||
String service,
|
||||
Service service,
|
||||
Multimap<String, String> params,
|
||||
Optional<Integer> jitterSeconds) {
|
||||
if (!jitterSeconds.isPresent() || jitterSeconds.get() <= 0) {
|
||||
@@ -199,7 +207,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
private Task createTaskWithDelay(
|
||||
String path,
|
||||
HttpMethod method,
|
||||
String service,
|
||||
Service service,
|
||||
Multimap<String, String> params,
|
||||
Duration delay) {
|
||||
if (delay.isEqual(Duration.ZERO)) {
|
||||
@@ -211,11 +219,11 @@ public class CloudTasksUtils implements Serializable {
|
||||
.build();
|
||||
}
|
||||
|
||||
public Task createPostTask(String path, String service, Multimap<String, String> params) {
|
||||
public Task createPostTask(String path, Service service, Multimap<String, String> params) {
|
||||
return createTask(path, HttpMethod.POST, service, params);
|
||||
}
|
||||
|
||||
public Task createGetTask(String path, String service, Multimap<String, String> params) {
|
||||
public Task createGetTask(String path, Service service, Multimap<String, String> params) {
|
||||
return createTask(path, HttpMethod.GET, service, params);
|
||||
}
|
||||
|
||||
@@ -224,7 +232,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
*/
|
||||
public Task createPostTaskWithJitter(
|
||||
String path,
|
||||
String service,
|
||||
Service service,
|
||||
Multimap<String, String> params,
|
||||
Optional<Integer> jitterSeconds) {
|
||||
return createTaskWithJitter(path, HttpMethod.POST, service, params, jitterSeconds);
|
||||
@@ -235,7 +243,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
*/
|
||||
public Task createGetTaskWithJitter(
|
||||
String path,
|
||||
String service,
|
||||
Service service,
|
||||
Multimap<String, String> params,
|
||||
Optional<Integer> jitterSeconds) {
|
||||
return createTaskWithJitter(path, HttpMethod.GET, service, params, jitterSeconds);
|
||||
@@ -243,13 +251,13 @@ public class CloudTasksUtils implements Serializable {
|
||||
|
||||
/** Create a {@link Task} via HTTP.POST that will be delayed for {@code delay}. */
|
||||
public Task createPostTaskWithDelay(
|
||||
String path, String service, Multimap<String, String> params, Duration delay) {
|
||||
String path, Service service, Multimap<String, String> params, Duration delay) {
|
||||
return createTaskWithDelay(path, HttpMethod.POST, service, params, delay);
|
||||
}
|
||||
|
||||
/** Create a {@link Task} via HTTP.GET that will be delayed for {@code delay}. */
|
||||
public Task createGetTaskWithDelay(
|
||||
String path, String service, Multimap<String, String> params, Duration delay) {
|
||||
String path, Service service, Multimap<String, String> params, Duration delay) {
|
||||
return createTaskWithDelay(path, HttpMethod.GET, service, params, delay);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -98,7 +98,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
/** Number of domains to retrieve and delete per SQL transaction. */
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
|
||||
@Inject
|
||||
@Parameter(PARAM_DRY_RUN)
|
||||
@@ -264,6 +264,6 @@ public class DeleteProberDataAction implements Runnable {
|
||||
// messages, or auto-renews because those will all be hard-deleted the next time the job runs
|
||||
// anyway.
|
||||
tm().putAll(ImmutableList.of(deletedDomain, historyEntry));
|
||||
dnsQueue.addDomainRefreshTask(deletedDomain.getDomainName());
|
||||
dnsUtils.requestDomainDnsRefresh(deletedDomain.getDomainName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.flows.custom.CustomLogicFactoryModule;
|
||||
import google.registry.flows.custom.CustomLogicModule;
|
||||
import google.registry.flows.domain.DomainPricingLogic;
|
||||
import google.registry.flows.domain.DomainPricingLogic.AllocationTokenInvalidForPremiumNameException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingEvent.Cancellation;
|
||||
import google.registry.model.billing.BillingEvent.Flag;
|
||||
@@ -53,6 +54,7 @@ import google.registry.util.Clock;
|
||||
import google.registry.util.SystemClock;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
@@ -138,9 +140,15 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
private final boolean isDryRun;
|
||||
private final boolean advanceCursor;
|
||||
private final Counter recurringsInScopeCounter =
|
||||
Metrics.counter("ExpandBilling", "RecurringsInScope");
|
||||
private final Counter expandedOneTimeCounter =
|
||||
Metrics.counter("ExpandBilling", "ExpandedOneTime");
|
||||
Metrics.counter("ExpandBilling", "Recurrings in scope for expansion");
|
||||
// Note that this counter is only accurate when running in dry run mode. Because SQL persistence
|
||||
// is a side effect and not idempotent, a transaction to save OneTimes could be successful but the
|
||||
// transform that contains it could be still be retried, rolling back the counter increment. The
|
||||
// same transform, when retried, would skip the already expanded OneTime, causing this counter to
|
||||
// be lower than it should be when not in dry run mode.
|
||||
// See: https://beam.apache.org/documentation/programming-guide/#user-code-idempotence
|
||||
private final Counter oneTimesToExpandCounter =
|
||||
Metrics.counter("ExpandBilling", "OneTimes that would be expanded");
|
||||
|
||||
ExpandRecurringBillingEventsPipeline(
|
||||
ExpandRecurringBillingEventsPipelineOptions options, Clock clock) {
|
||||
@@ -182,7 +190,7 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
+ "AND event_Time < :endTime "
|
||||
// Recurrence should not close before start time.
|
||||
+ "AND :startTime < recurrence_end_time "
|
||||
// Last expansion should happen at least one year before start time.
|
||||
// Last expansion should happen at least one year before end time.
|
||||
+ "AND recurrence_last_expansion < :oneYearAgo "
|
||||
// The recurrence should not close before next expansion time.
|
||||
+ "AND recurrence_last_expansion + INTERVAL '1 YEAR' < recurrence_end_time",
|
||||
@@ -195,6 +203,7 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
endTime.minusYears(1)),
|
||||
true,
|
||||
(BigInteger id) -> {
|
||||
recurringsInScopeCounter.inc();
|
||||
// Note that because all elements are mapped to the same dummy key, the next
|
||||
// batching transform will effectively be serial. This however does not matter for
|
||||
// our use case because the elements were obtained from a SQL read query, which
|
||||
@@ -239,20 +248,40 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
|
||||
private void expandOneRecurring(Long recurringId, ImmutableSet.Builder<ImmutableObject> results) {
|
||||
Recurring recurring = tm().loadByKey(Recurring.createVKey(recurringId));
|
||||
recurringsInScopeCounter.inc();
|
||||
Domain domain = tm().loadByKey(Domain.createVKey(recurring.getDomainRepoId()));
|
||||
Registry tld = Registry.get(domain.getTld());
|
||||
|
||||
// Determine the complete set of EventTimes this recurring event should expand to within
|
||||
// [max(recurrenceLastExpansion + 1 yr, startTime), min(recurrenceEndTime, endTime)).
|
||||
ImmutableSet<DateTime> eventTimes =
|
||||
ImmutableSet.copyOf(
|
||||
recurring
|
||||
.getRecurrenceTimeOfYear()
|
||||
.getInstancesInRange(
|
||||
Range.closedOpen(
|
||||
latestOf(recurring.getRecurrenceLastExpansion().plusYears(1), startTime),
|
||||
earliestOf(recurring.getRecurrenceEndTime(), endTime))));
|
||||
//
|
||||
// This range should always be legal for recurrings that are returned from the query. However,
|
||||
// it is possible that the recurring has changed between when the read transformation occurred
|
||||
// and now. This could be caused by some out-of-process mutations (such as a domain deletion
|
||||
// closing out a previously open-ended recurrence), or more subtly, Beam could execute the same
|
||||
// work multiple times due to transient communication issues between workers and the scheduler.
|
||||
// Such opportunistic retries are OK for pure functional transformations, but can cause
|
||||
// unexpected behavior when side effects are executed more than once. For example, the
|
||||
// recurrence_last_expansion field could be updated by a worker after a success expansion, which
|
||||
// failed to report the status to the scheduler in time, which in turn scheduled another worker
|
||||
// to work on the same batch. The second worker would see a new recurrence_last_expansion that
|
||||
// causes the range to be illegal.
|
||||
//
|
||||
// The best way to handle any unexpected behavior is to simply drop the recurring from
|
||||
// expansion, if its new state still calls for an expansion, it would be picked up the next time
|
||||
// the pipeline runs.
|
||||
ImmutableSet<DateTime> eventTimes;
|
||||
try {
|
||||
eventTimes =
|
||||
ImmutableSet.copyOf(
|
||||
recurring
|
||||
.getRecurrenceTimeOfYear()
|
||||
.getInstancesInRange(
|
||||
Range.closedOpen(
|
||||
latestOf(recurring.getRecurrenceLastExpansion().plusYears(1), startTime),
|
||||
earliestOf(recurring.getRecurrenceEndTime(), endTime))));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
Domain domain = tm().loadByKey(Domain.createVKey(recurring.getDomainRepoId()));
|
||||
Registry tld = Registry.get(domain.getTld());
|
||||
|
||||
// Find the times for which the OneTime billing event are already created, making this expansion
|
||||
// idempotent. There is no need to match to the domain repo ID as the cancellation matching
|
||||
@@ -277,7 +306,7 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
// Create new OneTime and DomainHistory for EventTimes that needs to be expanded.
|
||||
for (DateTime eventTime : eventTimesToExpand) {
|
||||
recurrenceLastExpansionTime = latestOf(recurrenceLastExpansionTime, eventTime);
|
||||
expandedOneTimeCounter.inc();
|
||||
oneTimesToExpandCounter.inc();
|
||||
DateTime billingTime = eventTime.plus(tld.getAutoRenewGracePeriodLength());
|
||||
// Note that the DomainHistory is created as of transaction time, as opposed to event time.
|
||||
// This might be counterintuitive because other DomainHistories are created at the time
|
||||
@@ -356,24 +385,31 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
// It is OK to always create a OneTime, even though the domain might be deleted or transferred
|
||||
// later during autorenew grace period, as a cancellation will always be written out in those
|
||||
// instances.
|
||||
OneTime oneTime =
|
||||
new OneTime.Builder()
|
||||
.setBillingTime(billingTime)
|
||||
.setRegistrarId(recurring.getRegistrarId())
|
||||
// Determine the cost for a one-year renewal.
|
||||
.setCost(
|
||||
domainPricingLogic
|
||||
.getRenewPrice(tld, recurring.getTargetId(), eventTime, 1, recurring)
|
||||
.getRenewCost())
|
||||
.setEventTime(eventTime)
|
||||
.setFlags(union(recurring.getFlags(), Flag.SYNTHETIC))
|
||||
.setDomainHistory(historyEntry)
|
||||
.setPeriodYears(1)
|
||||
.setReason(recurring.getReason())
|
||||
.setSyntheticCreationTime(endTime)
|
||||
.setCancellationMatchingBillingEvent(recurring)
|
||||
.setTargetId(recurring.getTargetId())
|
||||
.build();
|
||||
OneTime oneTime = null;
|
||||
try {
|
||||
oneTime =
|
||||
new OneTime.Builder()
|
||||
.setBillingTime(billingTime)
|
||||
.setRegistrarId(recurring.getRegistrarId())
|
||||
// Determine the cost for a one-year renewal.
|
||||
.setCost(
|
||||
domainPricingLogic
|
||||
.getRenewPrice(
|
||||
tld, recurring.getTargetId(), eventTime, 1, recurring, Optional.empty())
|
||||
.getRenewCost())
|
||||
.setEventTime(eventTime)
|
||||
.setFlags(union(recurring.getFlags(), Flag.SYNTHETIC))
|
||||
.setDomainHistory(historyEntry)
|
||||
.setPeriodYears(1)
|
||||
.setReason(recurring.getReason())
|
||||
.setSyntheticCreationTime(endTime)
|
||||
.setCancellationMatchingBillingEvent(recurring)
|
||||
.setTargetId(recurring.getTargetId())
|
||||
.build();
|
||||
} catch (AllocationTokenInvalidForPremiumNameException e) {
|
||||
// This should not be reached since we are not using an allocation token
|
||||
return;
|
||||
}
|
||||
results.add(oneTime);
|
||||
}
|
||||
results.add(
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.auto.value.AutoValue;
|
||||
import com.google.cloud.storage.BlobId;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.keyring.api.PgpHelper;
|
||||
import google.registry.model.common.Cursor;
|
||||
@@ -46,7 +47,6 @@ import google.registry.rde.RdeUtil;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.RequestParameters;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.xjc.rdeheader.XjcRdeHeader;
|
||||
import google.registry.xjc.rdeheader.XjcRdeHeaderElement;
|
||||
import google.registry.xml.ValidationMode;
|
||||
@@ -306,7 +306,7 @@ public class RdeIO {
|
||||
RDE_UPLOAD_QUEUE,
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
RdeUploadAction.PATH,
|
||||
Service.BACKEND.getServiceId(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.of(
|
||||
RequestParameters.PARAM_TLD,
|
||||
key.tld(),
|
||||
@@ -318,7 +318,7 @@ public class RdeIO {
|
||||
BRDA_QUEUE,
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
BrdaCopyAction.PATH,
|
||||
Service.BACKEND.getServiceId(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.of(
|
||||
RequestParameters.PARAM_TLD,
|
||||
key.tld(),
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Component;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryPipelineOptions;
|
||||
import google.registry.config.CloudTasksUtilsModule;
|
||||
@@ -62,7 +63,6 @@ import google.registry.rde.DepositFragment;
|
||||
import google.registry.rde.PendingDeposit;
|
||||
import google.registry.rde.PendingDeposit.PendingDepositCoder;
|
||||
import google.registry.rde.RdeMarshaller;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.UtilsModule;
|
||||
import google.registry.xml.ValidationMode;
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
@@ -19,18 +19,15 @@ import com.google.cloud.tasks.v2.CloudTasksClient;
|
||||
import com.google.cloud.tasks.v2.CloudTasksSettings;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.batch.CloudTasksUtils.GcpCloudTasksClient;
|
||||
import google.registry.batch.CloudTasksUtils.SerializableCloudTasksClient;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.CloudTasksUtils.GcpCloudTasksClient;
|
||||
import google.registry.util.CloudTasksUtils.SerializableCloudTasksClient;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Supplier;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* A {@link Module} that provides {@link CloudTasksUtils}.
|
||||
@@ -41,17 +38,6 @@ import javax.inject.Singleton;
|
||||
@Module
|
||||
public abstract class CloudTasksUtilsModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
public static CloudTasksUtils provideCloudTasksUtils(
|
||||
@Config("projectId") String projectId,
|
||||
@Config("locationId") String locationId,
|
||||
SerializableCloudTasksClient client,
|
||||
Retrier retrier,
|
||||
Clock clock) {
|
||||
return new CloudTasksUtils(retrier, clock, projectId, locationId, client);
|
||||
}
|
||||
|
||||
// Provides a supplier instead of using a Dagger @Provider because the latter is not serializable.
|
||||
@Provides
|
||||
public static Supplier<CloudTasksClient> provideCloudTasksClientSupplier(
|
||||
|
||||
@@ -106,6 +106,12 @@ public final class RegistryConfig {
|
||||
return config.gcpProject.projectId;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("serviceAccountEmails")
|
||||
public static ImmutableList<String> provideServiceAccountEmails(RegistryConfigSettings config) {
|
||||
return ImmutableList.copyOf(config.gcpProject.serviceAccountEmails);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("projectIdNumber")
|
||||
public static long provideProjectIdNumber(RegistryConfigSettings config) {
|
||||
@@ -924,7 +930,7 @@ public final class RegistryConfig {
|
||||
* <p>Note that this uses {@code @Named} instead of {@code @Config} so that it can be used from
|
||||
* the low-level util package, which cannot have a dependency on the config package.
|
||||
*
|
||||
* @see google.registry.util.CloudTasksUtils
|
||||
* @see google.registry.batch.CloudTasksUtils
|
||||
*/
|
||||
@Provides
|
||||
@Named("transientFailureRetries")
|
||||
|
||||
@@ -54,6 +54,7 @@ public class RegistryConfigSettings {
|
||||
public String backendServiceUrl;
|
||||
public String toolsServiceUrl;
|
||||
public String pubapiServiceUrl;
|
||||
public List<String> serviceAccountEmails;
|
||||
}
|
||||
|
||||
/** Configuration options for OAuth settings for authenticating users. */
|
||||
|
||||
@@ -22,6 +22,11 @@ gcpProject:
|
||||
backendServiceUrl: https://localhost
|
||||
toolsServiceUrl: https://localhost
|
||||
pubapiServiceUrl: https://localhost
|
||||
# Service accounts eligible for authorization (e.g. default service account,
|
||||
# account used by Cloud Scheduler) to send authenticated requests.
|
||||
serviceAccountEmails:
|
||||
- default-service-account-email@email.com
|
||||
- cloud-scheduler-email@email.com
|
||||
|
||||
gSuite:
|
||||
# Publicly accessible domain name of the running G Suite instance.
|
||||
@@ -427,7 +432,7 @@ misc:
|
||||
|
||||
beam:
|
||||
# The default region to run Apache Beam (Cloud Dataflow) jobs in.
|
||||
defaultJobRegion: us-east1
|
||||
defaultJobRegion: us-central1
|
||||
# The GCE machine type to use when a job is CPU-intensive (e. g. RDE). Be sure
|
||||
# to check the VM CPU quota for the job region. In a massively parallel
|
||||
# pipeline this quota can be easily reached and needs to be raised, otherwise
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
@@ -45,7 +46,6 @@ import google.registry.request.ParameterMap;
|
||||
import google.registry.request.RequestParameters;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
@@ -158,6 +158,6 @@ public final class TldFanoutAction implements Runnable {
|
||||
params.put(RequestParameters.PARAM_TLD, tld);
|
||||
}
|
||||
return cloudTasksUtils.createPostTaskWithJitter(
|
||||
endpoint, Service.BACKEND.toString(), params, jitterSeconds);
|
||||
endpoint, Service.BACKEND, params, jitterSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,5 +34,8 @@ public class DnsConstants {
|
||||
public static final String DNS_TARGET_CREATE_TIME_PARAM = "Create-Time";
|
||||
|
||||
/** The possible values of the {@code DNS_TARGET_TYPE_PARAM} parameter. */
|
||||
public enum TargetType { DOMAIN, HOST, ZONE }
|
||||
public enum TargetType {
|
||||
DOMAIN,
|
||||
HOST
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,11 +77,15 @@ public class DnsQueue {
|
||||
private static final RateLimiter rateLimiter = RateLimiter.create(9);
|
||||
|
||||
@Inject
|
||||
public DnsQueue(@Named(DNS_PULL_QUEUE_NAME) Queue queue, Clock clock) {
|
||||
DnsQueue(@Named(DNS_PULL_QUEUE_NAME) Queue queue, Clock clock) {
|
||||
this.queue = queue;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
Clock getClock() {
|
||||
return clock;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static DnsQueue createForTesting(Clock clock) {
|
||||
return new DnsQueue(getQueue(DNS_PULL_QUEUE_NAME), clock);
|
||||
@@ -108,7 +112,7 @@ public class DnsQueue {
|
||||
}
|
||||
|
||||
/** Adds a task to the queue to refresh the DNS information for the specified subordinate host. */
|
||||
public TaskHandle addHostRefreshTask(String hostName) {
|
||||
TaskHandle addHostRefreshTask(String hostName) {
|
||||
Optional<InternetDomainName> tld = Registries.findTldForName(InternetDomainName.from(hostName));
|
||||
checkArgument(
|
||||
tld.isPresent(), String.format("%s is not a subordinate host to a known tld", hostName));
|
||||
@@ -116,12 +120,12 @@ public class DnsQueue {
|
||||
}
|
||||
|
||||
/** Enqueues a task to refresh DNS for the specified domain now. */
|
||||
public TaskHandle addDomainRefreshTask(String domainName) {
|
||||
TaskHandle addDomainRefreshTask(String domainName) {
|
||||
return addDomainRefreshTask(domainName, Duration.ZERO);
|
||||
}
|
||||
|
||||
/** Enqueues a task to refresh DNS for the specified domain at some point in the future. */
|
||||
public TaskHandle addDomainRefreshTask(String domainName, Duration countdown) {
|
||||
TaskHandle addDomainRefreshTask(String domainName, Duration countdown) {
|
||||
return addToQueue(
|
||||
TargetType.DOMAIN,
|
||||
domainName,
|
||||
@@ -129,11 +133,6 @@ public class DnsQueue {
|
||||
countdown);
|
||||
}
|
||||
|
||||
/** Adds a task to the queue to refresh the DNS information for the specified zone. */
|
||||
public TaskHandle addZoneRefreshTask(String zoneName) {
|
||||
return addToQueue(TargetType.ZONE, zoneName, zoneName, Duration.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of tasks that can be leased with {@link #leaseTasks}.
|
||||
*
|
||||
|
||||
74
core/src/main/java/google/registry/dns/DnsUtils.java
Normal file
74
core/src/main/java/google/registry/dns/DnsUtils.java
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2023 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.dns;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.model.tld.Registries;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Utility class to handle DNS refresh requests. */
|
||||
// TODO: Make this a static util function once we are done with the DNS pull queue migration.
|
||||
public class DnsUtils {
|
||||
|
||||
private final DnsQueue dnsQueue;
|
||||
|
||||
@Inject
|
||||
DnsUtils(DnsQueue dnsQueue) {
|
||||
this.dnsQueue = dnsQueue;
|
||||
}
|
||||
|
||||
private void requestDnsRefresh(String name, TargetType type, Duration delay) {
|
||||
// Throws an IllegalArgumentException if the name is not under a managed TLD -- we only update
|
||||
// DNS for names that are under our management.
|
||||
String tld = Registries.findTldForNameOrThrow(InternetDomainName.from(name)).toString();
|
||||
if (usePullQueue()) {
|
||||
if (TargetType.HOST.equals(type)) {
|
||||
dnsQueue.addHostRefreshTask(name);
|
||||
} else {
|
||||
dnsQueue.addDomainRefreshTask(name, delay);
|
||||
}
|
||||
} else {
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().insert(
|
||||
new DnsRefreshRequest(
|
||||
type, name, tld, tm().getTransactionTime().plus(delay))));
|
||||
}
|
||||
}
|
||||
|
||||
public void requestDomainDnsRefresh(String domainName, Duration delay) {
|
||||
requestDnsRefresh(domainName, TargetType.DOMAIN, delay);
|
||||
}
|
||||
|
||||
public void requestDomainDnsRefresh(String domainName) {
|
||||
requestDomainDnsRefresh(domainName, Duration.ZERO);
|
||||
}
|
||||
|
||||
public void requestHostDnsRefresh(String hostName) {
|
||||
requestDnsRefresh(hostName, TargetType.HOST, Duration.ZERO);
|
||||
}
|
||||
|
||||
private boolean usePullQueue() {
|
||||
return !DatabaseMigrationStateSchedule.getValueAtTime(dnsQueue.getClock().nowUtc())
|
||||
.equals(MigrationState.DNS_SQL);
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import dagger.Lazy;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsMetrics.ActionStatus;
|
||||
import google.registry.dns.DnsMetrics.CommitStatus;
|
||||
@@ -54,7 +55,6 @@ import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.SendEmailService;
|
||||
@@ -85,7 +85,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final DnsQueue dnsQueue;
|
||||
private final DnsUtils dnsUtils;
|
||||
private final DnsWriterProxy dnsWriterProxy;
|
||||
private final DnsMetrics dnsMetrics;
|
||||
private final Duration timeout;
|
||||
@@ -139,7 +139,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
|
||||
@Header(APP_ENGINE_RETRY_HEADER) Optional<Integer> appEngineRetryCount,
|
||||
@Header(CLOUD_TASKS_RETRY_HEADER) Optional<Integer> cloudTasksRetryCount,
|
||||
DnsQueue dnsQueue,
|
||||
DnsUtils dnsUtils,
|
||||
DnsWriterProxy dnsWriterProxy,
|
||||
DnsMetrics dnsMetrics,
|
||||
LockHandler lockHandler,
|
||||
@@ -147,7 +147,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
SendEmailService sendEmailService,
|
||||
Response response) {
|
||||
this.dnsQueue = dnsQueue;
|
||||
this.dnsUtils = dnsUtils;
|
||||
this.dnsWriterProxy = dnsWriterProxy;
|
||||
this.dnsMetrics = dnsMetrics;
|
||||
this.timeout = timeout;
|
||||
@@ -339,7 +339,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
cloudTasksUtils.createPostTask(
|
||||
PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.<String, String>builder()
|
||||
.put(PARAM_TLD, tld)
|
||||
.put(PARAM_DNS_WRITER, dnsWriter)
|
||||
@@ -356,10 +356,10 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
private void requeueBatch() {
|
||||
logger.atInfo().log("Requeueing batch for retry.");
|
||||
for (String domain : nullToEmpty(domains)) {
|
||||
dnsQueue.addDomainRefreshTask(domain);
|
||||
dnsUtils.requestDomainDnsRefresh(domain);
|
||||
}
|
||||
for (String host : nullToEmpty(hosts)) {
|
||||
dnsQueue.addHostRefreshTask(host);
|
||||
dnsUtils.requestHostDnsRefresh(host);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.tld.Registries;
|
||||
@@ -53,7 +54,6 @@ import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
@@ -372,7 +372,7 @@ public final class ReadDnsQueueAction implements Runnable {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
PublishDnsUpdatesAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.<String, String>builder()
|
||||
.put(PARAM_TLD, tld)
|
||||
.put(PARAM_DNS_WRITER, dnsWriter)
|
||||
|
||||
@@ -39,7 +39,7 @@ import javax.inject.Inject;
|
||||
public final class RefreshDnsAction implements Runnable {
|
||||
|
||||
private final Clock clock;
|
||||
private final DnsQueue dnsQueue;
|
||||
private final DnsUtils dnsUtils;
|
||||
private final String domainOrHostName;
|
||||
private final TargetType type;
|
||||
|
||||
@@ -48,11 +48,11 @@ public final class RefreshDnsAction implements Runnable {
|
||||
@Parameter("domainOrHostName") String domainOrHostName,
|
||||
@Parameter("type") TargetType type,
|
||||
Clock clock,
|
||||
DnsQueue dnsQueue) {
|
||||
DnsUtils dnsUtils) {
|
||||
this.domainOrHostName = domainOrHostName;
|
||||
this.type = type;
|
||||
this.clock = clock;
|
||||
this.dnsQueue = dnsQueue;
|
||||
this.dnsUtils = dnsUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,11 +63,11 @@ public final class RefreshDnsAction implements Runnable {
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
loadAndVerifyExistence(Domain.class, domainOrHostName);
|
||||
dnsQueue.addDomainRefreshTask(domainOrHostName);
|
||||
dnsUtils.requestDomainDnsRefresh(domainOrHostName);
|
||||
break;
|
||||
case HOST:
|
||||
verifyHostIsSubordinate(loadAndVerifyExistence(Host.class, domainOrHostName));
|
||||
dnsQueue.addHostRefreshTask(domainOrHostName);
|
||||
dnsUtils.requestHostDnsRefresh(domainOrHostName);
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("Unsupported type: " + type);
|
||||
|
||||
@@ -45,14 +45,14 @@ public class RefreshDnsOnHostRenameAction implements Runnable {
|
||||
|
||||
private final VKey<Host> hostKey;
|
||||
private final Response response;
|
||||
private final DnsQueue dnsQueue;
|
||||
private final DnsUtils dnsUtils;
|
||||
|
||||
@Inject
|
||||
RefreshDnsOnHostRenameAction(
|
||||
@Parameter(PARAM_HOST_KEY) String hostKey, Response response, DnsQueue dnsQueue) {
|
||||
@Parameter(PARAM_HOST_KEY) String hostKey, Response response, DnsUtils dnsUtils) {
|
||||
this.hostKey = VKey.createEppVKeyFromString(hostKey);
|
||||
this.response = response;
|
||||
this.dnsQueue = dnsQueue;
|
||||
this.dnsUtils = dnsUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,7 +76,7 @@ public class RefreshDnsOnHostRenameAction implements Runnable {
|
||||
.stream()
|
||||
.map(domainKey -> tm().loadByKey(domainKey))
|
||||
.filter(Domain::shouldPublishToDns)
|
||||
.forEach(domain -> dnsQueue.addDomainRefreshTask(domain.getDomainName()));
|
||||
.forEach(domain -> dnsUtils.requestDomainDnsRefresh(domain.getDomainName()));
|
||||
}
|
||||
|
||||
if (!hostValid) {
|
||||
|
||||
@@ -1,150 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cronentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<schedule>every day 00:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 0 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<name>resaveAllEppResourcesPipeline</name>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<!--Deviation from cron tasks schedule: 1st monday of month 09:00 is replaced
|
||||
with 1st of the month 09:00 -->
|
||||
<schedule>0 9 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<name>expandRecurringBillingEvents</name>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>every day 03:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<!-- TODO: Add borgmon job to check that these files are created and updated successfully. -->
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Premium terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
@@ -6,6 +6,7 @@
|
||||
<mode>pull</mode>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for publishing DNS updates in batches. -->
|
||||
<queue>
|
||||
<name>dns-publish</name>
|
||||
<rate>100/s</rate>
|
||||
@@ -18,6 +19,7 @@
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for uploading RDE deposits to the escrow provider. -->
|
||||
<queue>
|
||||
<name>rde-upload</name>
|
||||
<rate>10/m</rate>
|
||||
@@ -28,6 +30,7 @@
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for uploading RDE reports to ICANN. -->
|
||||
<queue>
|
||||
<name>rde-report</name>
|
||||
<rate>1/s</rate>
|
||||
@@ -37,6 +40,7 @@
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for copying BRDA deposits to GCS. -->
|
||||
<queue>
|
||||
<name>brda</name>
|
||||
<rate>1/m</rate>
|
||||
@@ -74,7 +78,7 @@
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks to produce LORDN CSV reports, either by the query or queue method. -->
|
||||
<!-- Queue for tasks to produce LORDN CSV reports, populated by a Cloud Scheduler fanout job. -->
|
||||
<queue>
|
||||
<name>nordn</name>
|
||||
<rate>1/s</rate>
|
||||
@@ -84,18 +88,6 @@
|
||||
</retry-parameters>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for LORDN Claims CSV rows to be periodically queried and then uploaded in batches. -->
|
||||
<queue>
|
||||
<name>lordn-claims</name>
|
||||
<mode>pull</mode>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for LORDN Sunrise CSV rows to be periodically queried and then uploaded in batches. -->
|
||||
<queue>
|
||||
<name>lordn-sunrise</name>
|
||||
<mode>pull</mode>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that sync data to Google Spreadsheets. -->
|
||||
<queue>
|
||||
<name>sheet</name>
|
||||
|
||||
@@ -94,6 +94,12 @@
|
||||
<url-pattern>/registry-lock-verify</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Registrar console endpoints -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>frontend-servlet</servlet-name>
|
||||
<url-pattern>/console-api/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Security config -->
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cronentries>
|
||||
<taskentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
@@ -11,8 +11,9 @@
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
@@ -23,7 +24,6 @@
|
||||
cursor is lagging behind, so it'll catch up to the current date as quickly as
|
||||
possible. The only job that'll run under normal circumstances is the one that's
|
||||
close to midnight, since if the cursor is up-to-date, the task is a no-op.
|
||||
|
||||
We want it to be close to midnight because that reduces the chance that the
|
||||
point-in-time code won't have to go to the extra trouble of fetching old
|
||||
versions of objects from the database. However, we don't want it to run too
|
||||
@@ -32,134 +32,132 @@
|
||||
we add a 4+ minute grace period to ensure the transactions cool down, since
|
||||
our queries are not transactional.
|
||||
-->
|
||||
<schedule>every 4 hours from 00:07 to 20:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-report&endpoint=/_dr/task/rdeReport&forEachRealTld]]></url>
|
||||
<name>rdeReport</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeReportCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<!-- TODO: Add borgmon job to check that these files are created and updated successfully. -->
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<!--
|
||||
The next two wipeout jobs are required when crash has production data.
|
||||
-->
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<name>wipeOutCloudSql</name>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>every saturday 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
<schedule>7 3 * * 6</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
@@ -7,7 +7,7 @@
|
||||
<sessions-enabled>true</sessions-enabled>
|
||||
<instance-class>B4_1G</instance-class>
|
||||
<manual-scaling>
|
||||
<instances>12</instances>
|
||||
<instances>24</instances>
|
||||
</manual-scaling>
|
||||
|
||||
<system-properties>
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cronentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
@@ -23,7 +13,6 @@
|
||||
cursor is lagging behind, so it'll catch up to the current date as quickly as
|
||||
possible. The only job that'll run under normal circumstances is the one that's
|
||||
close to midnight, since if the cursor is up-to-date, the task is a no-op.
|
||||
|
||||
We want it to be close to midnight because that reduces the chance that the
|
||||
point-in-time code won't have to go to the extra trouble of fetching old
|
||||
versions of objects from the database. However, we don't want it to run too
|
||||
@@ -32,222 +21,221 @@
|
||||
we add a 4+ minute grace period to ensure the transactions cool down, since
|
||||
our queries are not transactional.
|
||||
-->
|
||||
<schedule>every 8 hours from 00:07 to 20:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 */8 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-report&endpoint=/_dr/task/rdeReport&forEachRealTld]]></url>
|
||||
<name>rdeReport</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeReportCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 0,12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<name>resaveAllEppResourcesPipeline</name>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
<!--
|
||||
Deviation from cron tasks schedule: 1st monday of month 09:00 is replaced
|
||||
with 1st of the month 09:00
|
||||
-->
|
||||
<schedule>0 9 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/updateRegistrarRdapBaseUrls]]></url>
|
||||
<name>updateRegistrarRdapBaseUrls</name>
|
||||
<description>
|
||||
This job reloads all registrar RDAP base URLs from ICANN.
|
||||
</description>
|
||||
<schedule>every day 02:34</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>34 2 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
<name>exportDomainLists</name>
|
||||
<description>
|
||||
This job exports lists of all active domain names to Google Drive and Google Cloud Storage.
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<name>expandRecurringBillingEvents</name>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>every day 03:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/sendExpiringCertificateNotificationEmail]]></url>
|
||||
<name>sendExpiringCertificateNotificationEmail</name>
|
||||
<description>
|
||||
This job runs an action that sends emails to partners if their certificates are expiring soon.
|
||||
</description>
|
||||
<schedule>every day 04:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>30 4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordn-phase=sunrise]]></url>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordnPhase=sunrise]]></url>
|
||||
<name>nordnUploadSunrise</name>
|
||||
<description>
|
||||
This job uploads LORDN Sunrise CSV files for each TLD to MarksDB. It should be
|
||||
run at most every three hours, or at absolute minimum every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordn-phase=claims]]></url>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=nordn&endpoint=/_dr/task/nordnUpload&forEachRealTld&lordnPhase=claims]]></url>
|
||||
<name>nordnUploadClaims</name>
|
||||
<description>
|
||||
This job uploads LORDN Claims CSV files for each TLD to MarksDB. It should be
|
||||
run at most every three hours, or at absolute minimum every 26 hours.
|
||||
</description>
|
||||
<!-- This may be set anywhere between "every 3 hours" and "every 25 hours". -->
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingStaging&runInEmpty]]></url>
|
||||
<name>icannReportingStaging</name>
|
||||
<description>
|
||||
Create ICANN activity and transaction reports for last month, storing them in
|
||||
gs://[PROJECT-ID]-reporting/icann/monthly/yyyy-MM
|
||||
Upon success, enqueues the icannReportingUpload task to POST these files to ICANN.
|
||||
</description>
|
||||
<schedule>2 of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 9 2 * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingUpload&runInEmpty]]></url>
|
||||
<name>icannReportingUpload</name>
|
||||
<description>
|
||||
Checks if the monthly ICANN reports have been successfully uploaded. If they have not, attempts to upload them again.
|
||||
Most of the time, this job should not do anything since the uploads are triggered when the reports are staged.
|
||||
However, in the event that an upload failed for any reason (e.g. ICANN server is down, IP allow list issues),
|
||||
this cron job will continue to retry uploads daily until they succeed.
|
||||
</description>
|
||||
<schedule>every day 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 15 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/generateInvoices?shouldPublish=true&runInEmpty]]></url>
|
||||
<name>generateInvoices</name>
|
||||
<description>
|
||||
Starts the beam/billing/InvoicingPipeline Dataflow template, which creates the overall invoice and
|
||||
detail report CSVs for last month, storing them in gs://[PROJECT-ID]-billing/invoices/yyyy-MM.
|
||||
@@ -260,29 +248,28 @@
|
||||
about 2 hours to complete, so we give 11 hours to be safe. Normally, we give 24+ hours (see
|
||||
icannReportingStaging), but the invoicing team prefers receiving the e-mail on the first of
|
||||
each month. -->
|
||||
<schedule>1 of month 19:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 19 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/generateSpec11&runInEmpty]]></url>
|
||||
<name>generateSpec11</name>
|
||||
<description>
|
||||
Starts the beam/spec11/Spec11Pipeline Dataflow template, which creates today's Spec11
|
||||
report. This report is stored in gs://[PROJECT-ID]-reporting/icann/spec11/yyyy-MM/.
|
||||
This job will only send email notifications on the second of every month.
|
||||
See GenerateSpec11ReportAction for more details.
|
||||
</description>
|
||||
<schedule>every day 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 15 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
|
||||
<name>wipeOutContactHistoryPii</name>
|
||||
<description>
|
||||
This job runs weekly to wipe out PII fields of ContactHistory entities
|
||||
that have been in the database for a certain period of time.
|
||||
</description>
|
||||
<schedule>every monday 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
<schedule>0 15 * * 1</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
@@ -1,70 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cronentries>
|
||||
|
||||
<cron>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<schedule>every day 00:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 0 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<name>resaveAllEppResourcesPipeline</name>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<!--Deviation from cron tasks schedule: 1st monday of month 09:00 is replaced
|
||||
with 1st of the month 09:00 -->
|
||||
<schedule>0 9 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<cron>
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<name>wipeOutCloudSql</name>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>every saturday 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
<schedule>7 3 * * 6</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
164
core/src/main/java/google/registry/env/sandbox/default/WEB-INF/cloud-scheduler-tasks.xml
vendored
Normal file
164
core/src/main/java/google/registry/env/sandbox/default/WEB-INF/cloud-scheduler-tasks.xml
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date eventually.
|
||||
-->
|
||||
<schedule>7 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<name>rdeUpload</name>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>0 */4 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<name>tmchDnl</name>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlAction, ClaimsList)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<name>tmchSmdrl</name>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlAction, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>15 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<name>tmchCrl</name>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlAction)
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<name>syncGroupMembers</name>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>0 */1 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
<name>exportDomainLists</name>
|
||||
<description>
|
||||
This job exports lists of all active domain names to Google Drive and Google Cloud Storage.
|
||||
</description>
|
||||
<schedule>0 */12 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<name>expandRecurringBillingEvents</name>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<name>resaveAllEppResourcesPipeline</name>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<!--
|
||||
Deviation from cron tasks schedule: 1st monday of month 09:00 is replaced
|
||||
with 1st of the month 09:00
|
||||
-->
|
||||
<schedule>0 9 1 * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<name>deleteProberData</name>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>0 14 * * 1</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<name>exportReservedTerms</name>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>30 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<name>exportPremiumTerms</name>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
|
||||
<name>wipeOutContactHistoryPii</name>
|
||||
<description>
|
||||
This job runs weekly to wipe out PII fields of ContactHistory entities
|
||||
that have been in the database for a certain period of time.
|
||||
</description>
|
||||
<schedule>0 15 * * 1</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
@@ -1,177 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
queue=<QUEUE_NAME>
|
||||
endpoint=<ENDPOINT_NAME> // URL Path of servlet, which may contain placeholders:
|
||||
runInEmpty // Run once, with no tld parameter
|
||||
forEachRealTld // Run for tlds with getTldType() == TldType.REAL
|
||||
forEachTestTld // Run for tlds with getTldType() == TldType.TEST
|
||||
exclude=TLD1[,TLD2] // exclude something otherwise included
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML document
|
||||
and streams it to cloud storage. When this job has finished successfully, it'll
|
||||
launch a separate task that uploads the deposit file to Iron Mountain via SFTP.
|
||||
</description>
|
||||
<!--
|
||||
This only needs to run once per day, but we launch additional jobs in case the
|
||||
cursor is lagging behind, so it'll catch up to the current date eventually.
|
||||
|
||||
See <a href="../../../production/default/WEB-INF/cron.xml">production config</a> for an
|
||||
explanation of job starting times.
|
||||
-->
|
||||
<schedule>every 12 hours from 00:07 to 12:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<description>
|
||||
This job is a no-op unless RdeUploadCursor falls behind for some reason.
|
||||
</description>
|
||||
<schedule>every 4 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchDnl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest DNL from MarksDB and inserts it into the database.
|
||||
(See: TmchDnlServlet, ClaimsList)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchSmdrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest SMDRL from MarksDB and inserts it into the database.
|
||||
(See: TmchSmdrlServlet, SignedMarkRevocationList)
|
||||
</description>
|
||||
<schedule>every 12 hours from 00:15 to 12:15</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=marksdb&endpoint=/_dr/task/tmchCrl&runInEmpty]]></url>
|
||||
<description>
|
||||
This job downloads the latest CRL from MarksDB and inserts it into the database.
|
||||
(See: TmchCrlServlet)
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/syncGroupMembers&runInEmpty]]></url>
|
||||
<description>
|
||||
Syncs RegistrarContact changes in the past hour to Google Groups.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<description>
|
||||
Synchronize Registrar entities to Google Spreadsheets.
|
||||
</description>
|
||||
<schedule>every 1 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<!-- TODO(b/249863289): disable until it is safe to run this pipeline
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/resaveAllEppResourcesPipeline?fast=true]]></url>
|
||||
<description>
|
||||
This job resaves all our resources, projected in time to "now".
|
||||
</description>
|
||||
<schedule>1st monday of month 09:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
-->
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportDomainLists&runInEmpty]]></url>
|
||||
<description>
|
||||
This job exports lists of all active domain names to Google Drive and Google Cloud Storage.
|
||||
</description>
|
||||
<schedule>every 12 hours synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/expandRecurringBillingEvents?advanceCursor]]></url>
|
||||
<description>
|
||||
This job runs an action that creates synthetic OneTime billing events from Recurring billing
|
||||
events. Events are created for all instances of Recurring billing events that should exist
|
||||
between the RECURRING_BILLING cursor's time and the execution time of the action.
|
||||
</description>
|
||||
<schedule>every day 03:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<description>
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>every day 03:07</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/deleteProberData&runInEmpty]]></url>
|
||||
<description>
|
||||
This job clears out data from probers and runs once a week.
|
||||
</description>
|
||||
<schedule>every monday 14:00</schedule>
|
||||
<timezone>UTC</timezone>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportReservedTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Reserved terms export to Google Drive job for creating once-daily exports.
|
||||
</description>
|
||||
<schedule>every day 05:30</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/exportPremiumTerms&forEachRealTld]]></url>
|
||||
<description>
|
||||
Exports premium price lists to the Google Drive folders for each TLD once per day.
|
||||
</description>
|
||||
<schedule>every day 05:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>every 1 minutes synchronized</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
|
||||
<description>
|
||||
This job runs weekly to wipe out PII fields of ContactHistory entities
|
||||
that have been in the database for a certain period of time.
|
||||
</description>
|
||||
<schedule>every monday 15:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
</cronentries>
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.EppException;
|
||||
@@ -116,6 +117,7 @@ import org.joda.time.DateTime;
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_CHECK)
|
||||
public final class DomainCheckFlow implements Flow {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject EppInput eppInput;
|
||||
@@ -271,6 +273,12 @@ public final class DomainCheckFlow implements Flow {
|
||||
|
||||
for (FeeCheckCommandExtensionItem feeCheckItem : feeCheck.getItems()) {
|
||||
for (String domainName : getDomainNamesToCheckForFee(feeCheckItem, domainNames.keySet())) {
|
||||
Optional<AllocationToken> defaultToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
Registry.get(InternetDomainName.from(domainName).parent().toString()),
|
||||
domainName,
|
||||
registrarId,
|
||||
now);
|
||||
FeeCheckResponseExtensionItem.Builder<?> builder = feeCheckItem.createResponseBuilder();
|
||||
Optional<Domain> domain = Optional.ofNullable(domainObjs.get(domainName));
|
||||
handleFeeRequest(
|
||||
@@ -281,7 +289,7 @@ public final class DomainCheckFlow implements Flow {
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
allocationToken,
|
||||
allocationToken.isPresent() ? allocationToken : defaultToken,
|
||||
availableDomains.contains(domainName),
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
responseItems.add(builder.setDomainNameIfSupported(domainName).build());
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
@@ -54,7 +52,6 @@ import static google.registry.model.tld.Registry.TldState.QUIET_PERIOD;
|
||||
import static google.registry.model.tld.Registry.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.model.tld.label.ReservationType.NAME_COLLISION;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
|
||||
@@ -62,9 +59,8 @@ import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
@@ -121,9 +117,7 @@ import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.model.tmch.ClaimsList;
|
||||
import google.registry.model.tmch.ClaimsListDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tmch.LordnTaskUtils;
|
||||
import java.util.Map;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
@@ -162,6 +156,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkExpiredException}
|
||||
* @error {@link DomainFlowTmchUtils.SignedMarkRevokedErrorException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.AcceptedTooLongAgoException}
|
||||
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
|
||||
@@ -231,7 +226,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
@Inject DomainCreateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainFlowTmchUtils tmchUtils;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
|
||||
@Inject DomainCreateFlow() {}
|
||||
|
||||
@Override
|
||||
@@ -269,6 +265,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
validateLaunchCreateNotice(launchCreate.get().getNotice(), domainLabel, isSuperuser, now);
|
||||
}
|
||||
boolean isSunriseCreate = hasSignedMarks && (tldState == START_DATE_SUNRISE);
|
||||
// TODO(sarahbot@): Add check for valid EPP actions on the token
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenCreateIfPresent(
|
||||
command,
|
||||
@@ -276,8 +273,13 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (!allocationToken.isPresent() && !registry.getDefaultPromoTokens().isEmpty()) {
|
||||
allocationToken = checkForDefaultToken(registry, command);
|
||||
allocationToken =
|
||||
DomainFlowUtils.checkForDefaultToken(registry, command.getDomainName(), registrarId, now);
|
||||
if (allocationToken.isPresent()) {
|
||||
defaultTokenUsed = true;
|
||||
}
|
||||
}
|
||||
boolean isAnchorTenant =
|
||||
isAnchorTenant(
|
||||
@@ -336,7 +338,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getCreatePrice(
|
||||
registry, targetId, now, years, isAnchorTenant, allocationToken);
|
||||
validateFeeChallenge(feeCreate, feesAndCredits);
|
||||
validateFeeChallenge(feeCreate, feesAndCredits, defaultTokenUsed);
|
||||
Optional<SecDnsCreateExtension> secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
@@ -376,7 +378,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
reservationTypes.contains(NAME_COLLISION)
|
||||
? ImmutableSet.of(SERVER_HOLD)
|
||||
: ImmutableSet.of();
|
||||
Domain domain =
|
||||
Domain.Builder domainBuilder =
|
||||
new Domain.Builder()
|
||||
.setCreationRegistrarId(registrarId)
|
||||
.setPersistedCurrentSponsorRegistrarId(registrarId)
|
||||
@@ -396,7 +398,11 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.setContacts(command.getContacts())
|
||||
.addGracePeriod(
|
||||
GracePeriod.forBillingEvent(GracePeriodStatus.ADD, repoId, createBillingEvent))
|
||||
.build();
|
||||
.setLordnPhase(
|
||||
hasSignedMarks
|
||||
? LordnPhase.SUNRISE
|
||||
: hasClaimsNotice ? LordnPhase.CLAIMS : LordnPhase.NONE);
|
||||
Domain domain = domainBuilder.build();
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenType().equals(TokenType.PACKAGE)) {
|
||||
if (years > 1) {
|
||||
@@ -418,8 +424,9 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
allocationTokenFlowUtils.redeemToken(
|
||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||
}
|
||||
enqueueTasks(domain, hasSignedMarks, hasClaimsNotice);
|
||||
|
||||
if (domain.shouldPublishToDns()) {
|
||||
dnsUtils.requestDomainDnsRefresh(domain.getDomainName());
|
||||
}
|
||||
EntityChanges entityChanges =
|
||||
flowCustomLogic.beforeSave(
|
||||
DomainCreateFlowCustomLogic.BeforeSaveParameters.newBuilder()
|
||||
@@ -443,36 +450,6 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.build();
|
||||
}
|
||||
|
||||
private Optional<AllocationToken> checkForDefaultToken(
|
||||
Registry registry, DomainCommand.Create command) throws EppException {
|
||||
Map<VKey<AllocationToken>, Optional<AllocationToken>> tokens =
|
||||
AllocationToken.getAll(registry.getDefaultPromoTokens());
|
||||
ImmutableList<Optional<AllocationToken>> tokenList =
|
||||
registry.getDefaultPromoTokens().stream()
|
||||
.map(tokens::get)
|
||||
.filter(Optional::isPresent)
|
||||
.collect(toImmutableList());
|
||||
checkState(
|
||||
!isNullOrEmpty(tokenList),
|
||||
"Failure while loading default TLD promotions from the database");
|
||||
// Check if any of the tokens are valid for this domain registration
|
||||
for (Optional<AllocationToken> token : tokenList) {
|
||||
try {
|
||||
AllocationTokenFlowUtils.validateToken(
|
||||
InternetDomainName.from(command.getDomainName()),
|
||||
token.get(),
|
||||
registrarId,
|
||||
tm().getTransactionTime());
|
||||
} catch (AssociationProhibitsOperationException e) {
|
||||
// Allocation token was not valid for this registration, continue to check the next token in
|
||||
// the list
|
||||
continue;
|
||||
}
|
||||
// Only use the first valid token in the list
|
||||
return token;
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
/**
|
||||
* Verifies that signed marks are only sent during sunrise.
|
||||
*
|
||||
@@ -692,15 +669,6 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.build();
|
||||
}
|
||||
|
||||
private void enqueueTasks(Domain newDomain, boolean hasSignedMarks, boolean hasClaimsNotice) {
|
||||
if (newDomain.shouldPublishToDns()) {
|
||||
dnsQueue.addDomainRefreshTask(newDomain.getDomainName());
|
||||
}
|
||||
if (hasClaimsNotice || hasSignedMarks) {
|
||||
LordnTaskUtils.enqueueDomainTask(newDomain);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the {@link RenewalPriceBehavior} and the renewal price that needs be stored in the
|
||||
* {@link Recurring} billing events.
|
||||
|
||||
@@ -44,7 +44,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.batch.AsyncTaskEnqueuer;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
@@ -129,7 +129,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject Trid trid;
|
||||
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@@ -260,7 +260,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
// If there's a pending transfer, the gaining client's autorenew billing
|
||||
// event and poll message will already have been deleted in
|
||||
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
||||
dnsQueue.addDomainRefreshTask(existingDomain.getDomainName());
|
||||
dnsUtils.requestDomainDnsRefresh(existingDomain.getDomainName());
|
||||
|
||||
entitiesToSave.add(newDomain, domainHistory);
|
||||
EntityChanges entityChanges =
|
||||
|
||||
@@ -39,6 +39,7 @@ import static google.registry.model.tld.label.ReservationType.RESERVED_FOR_ANCHO
|
||||
import static google.registry.model.tld.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
@@ -63,6 +64,7 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.EppException.AuthorizationErrorException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.ObjectDoesNotExistException;
|
||||
@@ -72,6 +74,7 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
@@ -671,6 +674,8 @@ public class DomainFlowUtils {
|
||||
String feeClass = null;
|
||||
ImmutableList<Fee> fees = ImmutableList.of();
|
||||
switch (feeRequest.getCommandName()) {
|
||||
// TODO(sarahbot@): Add check of valid EPP actions on token before passing the token to the
|
||||
// fee request.
|
||||
case CREATE:
|
||||
// Don't return a create price for reserved names.
|
||||
if (isReserved(domainName, isSunrise) && !isAvailable) {
|
||||
@@ -695,7 +700,8 @@ public class DomainFlowUtils {
|
||||
builder.setAvailIfSupported(true);
|
||||
fees =
|
||||
pricingLogic
|
||||
.getRenewPrice(registry, domainNameString, now, years, recurringBillingEvent)
|
||||
.getRenewPrice(
|
||||
registry, domainNameString, now, years, recurringBillingEvent, allocationToken)
|
||||
.getFees();
|
||||
break;
|
||||
case RESTORE:
|
||||
@@ -778,12 +784,13 @@ public class DomainFlowUtils {
|
||||
*/
|
||||
public static void validateFeeChallenge(
|
||||
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
||||
FeesAndCredits feesAndCredits)
|
||||
FeesAndCredits feesAndCredits,
|
||||
boolean defaultTokenUsed)
|
||||
throws EppException {
|
||||
if (feesAndCredits.hasAnyPremiumFees() && !feeCommand.isPresent()) {
|
||||
throw new FeesRequiredForPremiumNameException();
|
||||
}
|
||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits);
|
||||
validateFeesAckedIfPresent(feeCommand, feesAndCredits, defaultTokenUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -795,7 +802,8 @@ public class DomainFlowUtils {
|
||||
*/
|
||||
public static void validateFeesAckedIfPresent(
|
||||
final Optional<? extends FeeTransformCommandExtension> feeCommand,
|
||||
FeesAndCredits feesAndCredits)
|
||||
FeesAndCredits feesAndCredits,
|
||||
boolean defaultTokenUsed)
|
||||
throws EppException {
|
||||
// Check for the case where a fee command extension was required but not provided.
|
||||
// This only happens when the total fees are non-zero and include custom fees requiring the
|
||||
@@ -856,7 +864,9 @@ public class DomainFlowUtils {
|
||||
// Checking if total amount is expected. Extra fees that we are not expecting may be passed in.
|
||||
// Or if there is only a single fee type expected.
|
||||
if (!feeTotal.equals(feesAndCredits.getTotalCost())) {
|
||||
throw new FeesMismatchException(feesAndCredits.getTotalCost());
|
||||
if (!defaultTokenUsed || feeTotal.isLessThan(feesAndCredits.getTotalCost())) {
|
||||
throw new FeesMismatchException(feesAndCredits.getTotalCost());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1187,6 +1197,44 @@ public class DomainFlowUtils {
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a valid default token to be used for a domain create command.
|
||||
*
|
||||
* <p>If there is more than one valid default token for the registration, only the first valid
|
||||
* token found on the TLD's default token list will be returned.
|
||||
*/
|
||||
public static Optional<AllocationToken> checkForDefaultToken(
|
||||
Registry registry, String domainName, String registrarId, DateTime now) throws EppException {
|
||||
if (isNullOrEmpty(registry.getDefaultPromoTokens())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Map<VKey<AllocationToken>, Optional<AllocationToken>> tokens =
|
||||
AllocationToken.getAll(registry.getDefaultPromoTokens());
|
||||
ImmutableList<Optional<AllocationToken>> tokenList =
|
||||
registry.getDefaultPromoTokens().stream()
|
||||
.map(tokens::get)
|
||||
.filter(Optional::isPresent)
|
||||
.collect(toImmutableList());
|
||||
checkState(
|
||||
!isNullOrEmpty(tokenList),
|
||||
"Failure while loading default TLD promotions from the database");
|
||||
// Check if any of the tokens are valid for this domain registration
|
||||
for (Optional<AllocationToken> token : tokenList) {
|
||||
try {
|
||||
AllocationTokenFlowUtils.validateToken(
|
||||
InternetDomainName.from(domainName), token.get(), registrarId, now);
|
||||
} catch (AssociationProhibitsOperationException | StatusProhibitsOperationException e) {
|
||||
// Allocation token was not valid for this registration, continue to check the next token in
|
||||
// the list
|
||||
continue;
|
||||
}
|
||||
// Only use the first valid token in the list
|
||||
return token;
|
||||
}
|
||||
// No valid default token found
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Resource linked to this domain does not exist. */
|
||||
static class LinkedResourcesDoNotExistException extends ObjectDoesNotExistException {
|
||||
public LinkedResourcesDoNotExistException(Class<?> type, ImmutableSet<String> resourceIds) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import google.registry.model.domain.fee.BaseFee;
|
||||
import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
|
||||
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
|
||||
import google.registry.model.tld.Registry;
|
||||
import java.math.RoundingMode;
|
||||
@@ -112,21 +113,22 @@ public final class DomainPricingLogic {
|
||||
String domainName,
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
@Nullable Recurring recurringBillingEvent) {
|
||||
@Nullable Recurring recurringBillingEvent,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
checkArgument(years > 0, "Number of years must be positive");
|
||||
Money renewCost;
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
boolean isRenewCostPremiumPrice;
|
||||
// recurring billing event is null if the domain is still available. Billing events are created
|
||||
// in the process of domain creation.
|
||||
if (recurringBillingEvent == null) {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
renewCost = domainPrices.getRenewCost().multipliedBy(years);
|
||||
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken);
|
||||
isRenewCostPremiumPrice = domainPrices.isPremium();
|
||||
} else {
|
||||
switch (recurringBillingEvent.getRenewalPriceBehavior()) {
|
||||
case DEFAULT:
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
renewCost = domainPrices.getRenewCost().multipliedBy(years);
|
||||
renewCost = getDomainRenewCostWithDiscount(domainPrices, years, allocationToken);
|
||||
isRenewCostPremiumPrice = domainPrices.isPremium();
|
||||
break;
|
||||
// if the renewal price behavior is specified, then the renewal price should be the same
|
||||
@@ -136,6 +138,7 @@ public final class DomainPricingLogic {
|
||||
recurringBillingEvent.getRenewalPrice(),
|
||||
"Unexpected behavior: renewal price cannot be null when renewal behavior is"
|
||||
+ " SPECIFIED");
|
||||
// Don't apply allocation token to renewal price when SPECIFIED
|
||||
renewCost = recurringBillingEvent.getRenewalPrice().get().multipliedBy(years);
|
||||
isRenewCostPremiumPrice = false;
|
||||
break;
|
||||
@@ -143,9 +146,11 @@ public final class DomainPricingLogic {
|
||||
// at standard price of domains at the time, even if the domain is premium
|
||||
case NONPREMIUM:
|
||||
renewCost =
|
||||
Registry.get(getTldFromDomainName(domainName))
|
||||
.getStandardRenewCost(dateTime)
|
||||
.multipliedBy(years);
|
||||
getDomainCostWithDiscount(
|
||||
false,
|
||||
years,
|
||||
allocationToken,
|
||||
Registry.get(getTldFromDomainName(domainName)).getStandardRenewCost(dateTime));
|
||||
isRenewCostPremiumPrice = false;
|
||||
break;
|
||||
default:
|
||||
@@ -202,7 +207,7 @@ public final class DomainPricingLogic {
|
||||
@Nullable Recurring recurringBillingEvent)
|
||||
throws EppException {
|
||||
FeesAndCredits renewPrice =
|
||||
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent);
|
||||
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent, Optional.empty());
|
||||
return customLogic.customizeTransferPrice(
|
||||
TransferPriceParameters.newBuilder()
|
||||
.setFeesAndCredits(
|
||||
@@ -242,25 +247,40 @@ public final class DomainPricingLogic {
|
||||
private Money getDomainCreateCostWithDiscount(
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)
|
||||
throws EppException {
|
||||
return getDomainCostWithDiscount(
|
||||
domainPrices.isPremium(), years, allocationToken, domainPrices.getCreateCost());
|
||||
}
|
||||
|
||||
/** Returns the domain renew cost with allocation-token-related discounts applied. */
|
||||
private Money getDomainRenewCostWithDiscount(
|
||||
DomainPrices domainPrices, int years, Optional<AllocationToken> allocationToken)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
return getDomainCostWithDiscount(
|
||||
domainPrices.isPremium(), years, allocationToken, domainPrices.getRenewCost());
|
||||
}
|
||||
|
||||
private Money getDomainCostWithDiscount(
|
||||
boolean isPremium, int years, Optional<AllocationToken> allocationToken, Money oneYearCost)
|
||||
throws AllocationTokenInvalidForPremiumNameException {
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getDiscountFraction() != 0.0
|
||||
&& domainPrices.isPremium()
|
||||
&& isPremium
|
||||
&& !allocationToken.get().shouldDiscountPremiums()) {
|
||||
throw new AllocationTokenInvalidForPremiumNameException();
|
||||
}
|
||||
Money oneYearCreateCost = domainPrices.getCreateCost();
|
||||
Money totalDomainCreateCost = oneYearCreateCost.multipliedBy(years);
|
||||
Money totalDomainFlowCost = oneYearCost.multipliedBy(years);
|
||||
|
||||
// Apply the allocation token discount, if applicable.
|
||||
if (allocationToken.isPresent()) {
|
||||
if (allocationToken.isPresent()
|
||||
&& allocationToken.get().getTokenBehavior().equals(TokenBehavior.DEFAULT)) {
|
||||
int discountedYears = Math.min(years, allocationToken.get().getDiscountYears());
|
||||
Money discount =
|
||||
oneYearCreateCost.multipliedBy(
|
||||
oneYearCost.multipliedBy(
|
||||
discountedYears * allocationToken.get().getDiscountFraction(),
|
||||
RoundingMode.HALF_EVEN);
|
||||
totalDomainCreateCost = totalDomainCreateCost.minus(discount);
|
||||
totalDomainFlowCost = totalDomainFlowCost.minus(discount);
|
||||
}
|
||||
return totalDomainCreateCost;
|
||||
return totalDomainFlowCost;
|
||||
}
|
||||
|
||||
/** An allocation token was provided that is invalid for premium domains. */
|
||||
|
||||
@@ -172,6 +172,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
Renew command = (Renew) resourceCommand;
|
||||
// Loads the target resource if it exists
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
// TODO(sarahbot@): Add check for valid EPP actions on the token
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
@@ -198,8 +199,9 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
targetId,
|
||||
now,
|
||||
years,
|
||||
existingRecurringBillingEvent);
|
||||
validateFeeChallenge(feeRenew, feesAndCredits);
|
||||
existingRecurringBillingEvent,
|
||||
allocationToken);
|
||||
validateFeeChallenge(feeRenew, feesAndCredits, false);
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder()
|
||||
.setExistingDomain(existingDomain)
|
||||
|
||||
@@ -34,7 +34,7 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
@@ -122,7 +122,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainRestoreRequestFlow() {}
|
||||
@@ -186,7 +186,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
entitiesToSave.add(newDomain, domainHistory, autorenewEvent, autorenewPollMessage);
|
||||
tm().putAll(entitiesToSave.build());
|
||||
tm().delete(existingDomain.getDeletePollMessage());
|
||||
dnsQueue.addDomainRefreshTask(existingDomain.getDomainName());
|
||||
dnsUtils.requestDomainDnsRefresh(existingDomain.getDomainName());
|
||||
return responseBuilder
|
||||
.setExtensions(createResponseExtensions(feesAndCredits, feeUpdate, isExpired))
|
||||
.build();
|
||||
@@ -226,7 +226,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
if (!existingDomain.getGracePeriodStatuses().contains(GracePeriodStatus.REDEMPTION)) {
|
||||
throw new DomainNotEligibleForRestoreException();
|
||||
}
|
||||
validateFeeChallenge(feeUpdate, feesAndCredits);
|
||||
validateFeeChallenge(feeUpdate, feesAndCredits, false);
|
||||
}
|
||||
|
||||
private static Domain performRestore(
|
||||
|
||||
@@ -210,7 +210,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
}
|
||||
|
||||
if (feesAndCredits.isPresent()) {
|
||||
validateFeeChallenge(feeTransfer, feesAndCredits.get());
|
||||
validateFeeChallenge(feeTransfer, feesAndCredits.get(), false);
|
||||
}
|
||||
HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
|
||||
historyBuilder
|
||||
|
||||
@@ -49,7 +49,7 @@ import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.RegistrarId;
|
||||
@@ -156,7 +156,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject Trid trid;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainUpdateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@@ -183,7 +183,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
|
||||
validateNewState(newDomain);
|
||||
if (requiresDnsUpdate(existingDomain, newDomain)) {
|
||||
dnsQueue.addDomainRefreshTask(targetId);
|
||||
dnsUtils.requestDomainDnsRefresh(targetId);
|
||||
}
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(newDomain, domainHistory);
|
||||
@@ -231,7 +231,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
Optional<FeeUpdateCommandExtension> feeUpdate =
|
||||
eppInput.getSingleExtension(FeeUpdateCommandExtension.class);
|
||||
FeesAndCredits feesAndCredits = pricingLogic.getUpdatePrice(registry, targetId, now);
|
||||
validateFeesAckedIfPresent(feeUpdate, feesAndCredits);
|
||||
validateFeesAckedIfPresent(feeUpdate, feesAndCredits, false);
|
||||
verifyNotInPendingDelete(
|
||||
add.getContacts(),
|
||||
command.getInnerChange().getRegistrant(),
|
||||
|
||||
@@ -28,7 +28,7 @@ import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
@@ -85,7 +85,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
@Inject @RegistrarId String registrarId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject HostHistory.Builder historyBuilder;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
|
||||
@Inject
|
||||
@@ -138,7 +138,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
.build());
|
||||
// Only update DNS if this is a subordinate host. External hosts have no glue to write, so
|
||||
// they are only written as NS records from the referencing domain.
|
||||
dnsQueue.addHostRefreshTask(targetId);
|
||||
dnsUtils.requestHostDnsRefresh(targetId);
|
||||
}
|
||||
tm().insertAll(entitiesToSave);
|
||||
return responseBuilder.setResData(HostCreateData.create(targetId, now)).build();
|
||||
|
||||
@@ -24,7 +24,7 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.RegistrarId;
|
||||
@@ -71,7 +71,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_DELETE_PROHIBITED);
|
||||
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject @RegistrarId String registrarId;
|
||||
@Inject @TargetId String targetId;
|
||||
@@ -104,7 +104,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
}
|
||||
Host newHost = existingHost.asBuilder().setStatusValues(null).setDeletionTime(now).build();
|
||||
if (existingHost.isSubordinate()) {
|
||||
dnsQueue.addHostRefreshTask(existingHost.getHostName());
|
||||
dnsUtils.requestHostDnsRefresh(existingHost.getHostName());
|
||||
tm().update(
|
||||
tm().loadByKey(existingHost.getSuperordinateDomain())
|
||||
.asBuilder()
|
||||
|
||||
@@ -36,7 +36,8 @@ import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.batch.AsyncTaskEnqueuer;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.dns.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.ObjectAlreadyExistsException;
|
||||
@@ -65,7 +66,6 @@ import google.registry.model.host.HostHistory;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -124,7 +124,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject HostHistory.Builder historyBuilder;
|
||||
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@@ -266,21 +266,21 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
// Only update DNS for subordinate hosts. External hosts have no glue to write, so they
|
||||
// are only written as NS records from the referencing domain.
|
||||
if (existingHost.isSubordinate()) {
|
||||
dnsQueue.addHostRefreshTask(existingHost.getHostName());
|
||||
dnsUtils.requestHostDnsRefresh(existingHost.getHostName());
|
||||
}
|
||||
// In case of a rename, there are many updates we need to queue up.
|
||||
if (((Update) resourceCommand).getInnerChange().getHostName() != null) {
|
||||
// If the renamed host is also subordinate, then we must enqueue an update to write the new
|
||||
// glue.
|
||||
if (newHost.isSubordinate()) {
|
||||
dnsQueue.addHostRefreshTask(newHost.getHostName());
|
||||
dnsUtils.requestHostDnsRefresh(newHost.getHostName());
|
||||
}
|
||||
// We must also enqueue updates for all domains that use this host as their nameserver so
|
||||
// that their NS records can be updated to point at the new name.
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTask(
|
||||
RefreshDnsOnHostRenameAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.of(PARAM_HOST_KEY, existingHost.createVKey().stringify()));
|
||||
cloudTasksUtils.enqueue(QUEUE_HOST_RENAME, task);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ U+011F # LATIN SMALL LETTER G WITH BREVE
|
||||
U+01E7 # LATIN SMALL LETTER G WITH CARON
|
||||
U+0121 # LATIN SMALL LETTER G WITH DOT ABOVE
|
||||
U+0123 # LATIN SMALL LETTER G WITH CEDILLA
|
||||
U+01E5 # LATIN SMALL LETTER G WITH STROKE
|
||||
U+0068 # LATIN SMALL LETTER H
|
||||
U+0127 # LATIN SMALL LETTER H WITH STROKE
|
||||
U+0069 # LATIN SMALL LETTER I
|
||||
|
||||
@@ -28,13 +28,13 @@ import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.protobuf.Timestamp;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
@@ -337,7 +337,7 @@ public class LoadTestAction implements Runnable {
|
||||
cloudTasksUtils
|
||||
.createPostTask(
|
||||
"/_dr/epptool",
|
||||
Service.TOOLS.toString(),
|
||||
Service.TOOLS,
|
||||
ImmutableMultimap.of(
|
||||
"clientId",
|
||||
registrarId,
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
@@ -28,6 +29,7 @@ import org.joda.time.DateTime;
|
||||
public class CreateAutoTimestamp extends ImmutableObject implements UnsafeSerializable {
|
||||
|
||||
@Column(nullable = false)
|
||||
@Expose
|
||||
DateTime creationTime;
|
||||
|
||||
@PrePersist
|
||||
|
||||
@@ -31,8 +31,8 @@ import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.dns.RefreshDnsAction;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -41,7 +41,6 @@ import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.AttributeOverride;
|
||||
@@ -67,7 +66,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc5730">RFC 5730</a>
|
||||
*/
|
||||
@Transient String repoId;
|
||||
@Expose @Transient String repoId;
|
||||
|
||||
/**
|
||||
* The ID of the registrar that is currently sponsoring this resource.
|
||||
@@ -75,7 +74,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* <p>This can be null in the case of pre-Registry-3.0-migration history objects with null
|
||||
* resource fields.
|
||||
*/
|
||||
String currentSponsorRegistrarId;
|
||||
@Expose String currentSponsorRegistrarId;
|
||||
|
||||
/**
|
||||
* The ID of the registrar that created this resource.
|
||||
@@ -83,7 +82,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* <p>This can be null in the case of pre-Registry-3.0-migration history objects with null
|
||||
* resource fields.
|
||||
*/
|
||||
String creationRegistrarId;
|
||||
@Expose String creationRegistrarId;
|
||||
|
||||
/**
|
||||
* The ID of the registrar that last updated this resource.
|
||||
@@ -92,7 +91,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* edits; it only includes EPP-visible modifications such as {@literal <update>}. Can be null if
|
||||
* the resource has never been modified.
|
||||
*/
|
||||
String lastEppUpdateRegistrarId;
|
||||
@Expose String lastEppUpdateRegistrarId;
|
||||
|
||||
/**
|
||||
* The time when this resource was created.
|
||||
@@ -106,6 +105,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
*/
|
||||
// Need to override the default non-null column attribute.
|
||||
@AttributeOverride(name = "creationTime", column = @Column)
|
||||
@Expose
|
||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
/**
|
||||
@@ -130,27 +130,10 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* edits; it only includes EPP-visible modifications such as {@literal <update>}. Can be null if
|
||||
* the resource has never been modified.
|
||||
*/
|
||||
DateTime lastEppUpdateTime;
|
||||
@Expose DateTime lastEppUpdateTime;
|
||||
|
||||
/** Status values associated with this resource. */
|
||||
Set<StatusValue> statuses;
|
||||
|
||||
/**
|
||||
* When this domain/host's DNS was requested to be refreshed, or null if its DNS is up-to-date.
|
||||
*
|
||||
* <p>This will almost always be null except in the couple of minutes' interval between when a
|
||||
* DNS-affecting create or update operation takes place and when the {@link RefreshDnsAction}
|
||||
* runs, which resets this back to null upon completion of the DNS refresh task. This is a {@link
|
||||
* DateTime} rather than a simple dirty boolean so that the DNS refresh action can order by the
|
||||
* DNS refresh request time and take action on the oldest ones first.
|
||||
*
|
||||
* <p>Note that in the {@code DomainHistory}/{@code HostHistory} table this value means something
|
||||
* slightly different: It means that the given domain/host action requested a DNS update. Unlike
|
||||
* on the {@code Domain}/{code Host} table, this value is not then subsequently nulled out once
|
||||
* the DNS refresh is complete; rather, it remains as a permanent record of which actions were
|
||||
* DNS-affecting and which were not.
|
||||
*/
|
||||
@Transient @Nullable protected DateTime dnsRefreshRequestTime;
|
||||
@Expose Set<StatusValue> statuses;
|
||||
|
||||
public String getRepoId() {
|
||||
return repoId;
|
||||
@@ -203,19 +186,6 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
return deletionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DNS refresh request time iff this domain/host's DNS needs refreshing, otherwise
|
||||
* absent.
|
||||
*/
|
||||
public Optional<DateTime> getDnsRefreshRequestTime() {
|
||||
return Optional.ofNullable(dnsRefreshRequestTime);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setInternalDnsRefreshRequestTime(DateTime time) {
|
||||
dnsRefreshRequestTime = time;
|
||||
}
|
||||
|
||||
/** Return a clone of the resource with timed status values modified using the given time. */
|
||||
public abstract EppResource cloneProjectedAtTime(DateTime now);
|
||||
|
||||
@@ -369,11 +339,6 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setDnsRefreshRequestTime(Optional<DateTime> dnsRefreshRequestTime) {
|
||||
getInstance().dnsRefreshRequestTime = dnsRefreshRequestTime.orElse(null);
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
/** Build the resource, nullifying empty strings and sets and setting defaults. */
|
||||
@Override
|
||||
public T build() {
|
||||
|
||||
@@ -44,6 +44,8 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static boolean useUncachedForTest = false;
|
||||
|
||||
public enum PrimaryDatabase {
|
||||
CLOUD_SQL,
|
||||
DATASTORE
|
||||
@@ -85,7 +87,13 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Toggles SQL Sequence based allocateId */
|
||||
SEQUENCE_BASED_ALLOCATE_ID(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
SEQUENCE_BASED_ALLOCATE_ID(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Use SQL-based Nordn upload flow instead of the pull queue-based one. */
|
||||
NORDN_SQL(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Use SQL-based DNS update flow instead of the pull queue-based one. */
|
||||
DNS_SQL(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
|
||||
private final PrimaryDatabase primaryDatabase;
|
||||
private final boolean isReadOnly;
|
||||
@@ -164,7 +172,13 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
MigrationState.SQL_ONLY,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY)
|
||||
.putAll(MigrationState.SQL_ONLY, MigrationState.SEQUENCE_BASED_ALLOCATE_ID);
|
||||
.putAll(MigrationState.SQL_ONLY, MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
|
||||
.putAll(MigrationState.SEQUENCE_BASED_ALLOCATE_ID, MigrationState.NORDN_SQL)
|
||||
.putAll(
|
||||
MigrationState.NORDN_SQL,
|
||||
MigrationState.SEQUENCE_BASED_ALLOCATE_ID,
|
||||
MigrationState.DNS_SQL)
|
||||
.putAll(MigrationState.DNS_SQL, MigrationState.NORDN_SQL);
|
||||
|
||||
// In addition, we can always transition from a state to itself (useful when updating the map).
|
||||
Arrays.stream(MigrationState.values()).forEach(state -> builder.put(state, state));
|
||||
@@ -181,8 +195,8 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
public TimedTransitionProperty<MigrationState> migrationTransitions =
|
||||
TimedTransitionProperty.withInitialValue(MigrationState.DATASTORE_ONLY);
|
||||
|
||||
// Required for Objectify initialization
|
||||
private DatabaseMigrationStateSchedule() {}
|
||||
// Required for Hibernate initialization
|
||||
protected DatabaseMigrationStateSchedule() {}
|
||||
|
||||
@VisibleForTesting
|
||||
public DatabaseMigrationStateSchedule(
|
||||
@@ -205,6 +219,11 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void useUncachedForTest() {
|
||||
useUncachedForTest = true;
|
||||
}
|
||||
|
||||
/** Loads the currently-set migration schedule from the cache, or the default if none exists. */
|
||||
public static TimedTransitionProperty<MigrationState> get() {
|
||||
return CACHE.get(DatabaseMigrationStateSchedule.class);
|
||||
@@ -212,7 +231,9 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
|
||||
/** Returns the database migration status at the given time. */
|
||||
public static MigrationState getValueAtTime(DateTime dateTime) {
|
||||
return get().getValueAtTime(dateTime);
|
||||
return useUncachedForTest
|
||||
? getUncached().getValueAtTime(dateTime)
|
||||
: get().getValueAtTime(dateTime);
|
||||
}
|
||||
|
||||
/** Loads the currently-set migration schedule from SQL, or the default if none exists. */
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright 2023 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.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.PublishDnsUpdatesAction;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Entity
|
||||
@Table(indexes = {@Index(columnList = "requestTime"), @Index(columnList = "lastProcessTime")})
|
||||
public class DnsRefreshRequest extends ImmutableObject {
|
||||
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Id
|
||||
@SuppressWarnings("unused")
|
||||
protected long id;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private TargetType type;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String tld;
|
||||
|
||||
@Column(nullable = false)
|
||||
private DateTime requestTime;
|
||||
|
||||
@Column(nullable = false)
|
||||
private DateTime lastProcessTime;
|
||||
|
||||
public TargetType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getTld() {
|
||||
return tld;
|
||||
}
|
||||
|
||||
public DateTime getRequestTime() {
|
||||
return requestTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time at which the entity was last processed.
|
||||
*
|
||||
* <p>Note that "processed" means that it was read, not necessarily that the DNS request was
|
||||
* processed successfully. The subsequent steps to bundle requests together and enqueue them in a
|
||||
* Cloud Tasks queue for {@link PublishDnsUpdatesAction} to process can still fail.
|
||||
*
|
||||
* <p>This value allows us to control if a row is just recently read and should be skipped, should
|
||||
* there are concurrent reads that all attempt to read the rows with oldest {@link #requestTime},
|
||||
* or another read that comes too early after the previous read.
|
||||
*/
|
||||
public DateTime getLastProcessTime() {
|
||||
return lastProcessTime;
|
||||
}
|
||||
|
||||
protected DnsRefreshRequest() {}
|
||||
|
||||
private DnsRefreshRequest(
|
||||
@Nullable Long id,
|
||||
TargetType type,
|
||||
String name,
|
||||
String tld,
|
||||
DateTime requestTime,
|
||||
DateTime lastProcessTime) {
|
||||
checkNotNull(type, "Target type cannot be null");
|
||||
checkNotNull(name, "Domain/host name cannot be null");
|
||||
checkNotNull(tld, "TLD cannot be null");
|
||||
checkNotNull(requestTime, "Request time cannot be null");
|
||||
checkNotNull(lastProcessTime, "Last process time cannot be null");
|
||||
if (id != null) {
|
||||
this.id = id;
|
||||
}
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.tld = tld;
|
||||
this.requestTime = requestTime;
|
||||
this.lastProcessTime = lastProcessTime;
|
||||
}
|
||||
|
||||
public DnsRefreshRequest(TargetType type, String name, String tld, DateTime requestTime) {
|
||||
this(null, type, name, tld, requestTime, START_OF_TIME);
|
||||
}
|
||||
|
||||
public DnsRefreshRequest updateProcessTime(DateTime processTime) {
|
||||
checkArgument(
|
||||
processTime.isAfter(getRequestTime()),
|
||||
"Process time %s must be later than request time %s",
|
||||
processTime,
|
||||
getRequestTime());
|
||||
checkArgument(
|
||||
processTime.isAfter(getLastProcessTime()),
|
||||
"New process time %s must be later than the old one %s",
|
||||
processTime,
|
||||
getLastProcessTime());
|
||||
return new DnsRefreshRequest(id, getType(), getName(), getTld(), getRequestTime(), processTime);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.UpdateAutoTimestampEntity;
|
||||
import google.registry.persistence.VKey;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
@@ -47,7 +48,6 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
private Long id;
|
||||
|
||||
/** GAIA ID associated with the user in question. */
|
||||
@Column(nullable = false)
|
||||
private String gaiaId;
|
||||
|
||||
/** Email address of the user in question. */
|
||||
@@ -118,6 +118,11 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<User> createVKey() {
|
||||
return VKey.create(User.class, getId());
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link User} objects. */
|
||||
public static class Builder extends Buildable.Builder<User> {
|
||||
|
||||
@@ -129,7 +134,6 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
|
||||
@Override
|
||||
public User build() {
|
||||
checkArgumentNotNull(getInstance().gaiaId, "Gaia ID cannot be null");
|
||||
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
|
||||
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
|
||||
return super.build();
|
||||
|
||||
@@ -58,7 +58,6 @@ import org.joda.time.DateTime;
|
||||
@Index(columnList = "techContact"),
|
||||
@Index(columnList = "tld"),
|
||||
@Index(columnList = "registrantContact"),
|
||||
@Index(columnList = "dnsRefreshRequestTime"),
|
||||
@Index(columnList = "lordnPhase"),
|
||||
@Index(columnList = "billing_recurrence_id"),
|
||||
@Index(columnList = "transfer_billing_event_id"),
|
||||
@@ -200,7 +199,6 @@ public class Domain extends DomainBase implements ForeignKeyedEppResource {
|
||||
.setSubordinateHosts(domainBase.getSubordinateHosts())
|
||||
.setStatusValues(domainBase.getStatusValues())
|
||||
.setTransferData(domainBase.getTransferData())
|
||||
.setDnsRefreshRequestTime(domainBase.getDnsRefreshRequestTime())
|
||||
.setLordnPhase(domainBase.getLordnPhase())
|
||||
.setCurrentPackageToken(domainBase.getCurrentPackageToken().orElse(null));
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.flows.ResourceFlowUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ResourceWithTransferData;
|
||||
@@ -121,20 +122,20 @@ public class DomainBase extends EppResource
|
||||
*
|
||||
* @invariant domainName == domainName.toLowerCase(Locale.ENGLISH)
|
||||
*/
|
||||
String domainName;
|
||||
@Expose String domainName;
|
||||
|
||||
/** The top level domain this is under, de-normalized from {@link #domainName}. */
|
||||
String tld;
|
||||
|
||||
/** References to hosts that are the nameservers for the domain. */
|
||||
@Transient Set<VKey<Host>> nsHosts;
|
||||
@Expose @Transient Set<VKey<Host>> nsHosts;
|
||||
|
||||
/** Contacts. */
|
||||
VKey<Contact> adminContact;
|
||||
@Expose VKey<Contact> adminContact;
|
||||
|
||||
VKey<Contact> billingContact;
|
||||
VKey<Contact> techContact;
|
||||
VKey<Contact> registrantContact;
|
||||
@Expose VKey<Contact> billingContact;
|
||||
@Expose VKey<Contact> techContact;
|
||||
@Expose VKey<Contact> registrantContact;
|
||||
|
||||
/** Authorization info (aka transfer secret) of the domain. */
|
||||
@Embedded
|
||||
@@ -175,10 +176,10 @@ public class DomainBase extends EppResource
|
||||
String idnTableName;
|
||||
|
||||
/** Fully qualified host names of this domain's active subordinate hosts. */
|
||||
Set<String> subordinateHosts;
|
||||
@Expose Set<String> subordinateHosts;
|
||||
|
||||
/** When this domain's registration will expire. */
|
||||
DateTime registrationExpirationTime;
|
||||
@Expose DateTime registrationExpirationTime;
|
||||
|
||||
/**
|
||||
* The poll message associated with this domain being deleted.
|
||||
@@ -230,7 +231,7 @@ public class DomainBase extends EppResource
|
||||
*
|
||||
* <p>Can be null if the resource has never been transferred.
|
||||
*/
|
||||
DateTime lastTransferTime;
|
||||
@Expose DateTime lastTransferTime;
|
||||
|
||||
/**
|
||||
* When the domain's autorenewal status will expire.
|
||||
@@ -273,13 +274,6 @@ public class DomainBase extends EppResource
|
||||
return lordnPhase;
|
||||
}
|
||||
|
||||
@Access(AccessType.PROPERTY)
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "dnsRefreshRequestTime")
|
||||
private DateTime getInternalDnsRefreshRequestTime() {
|
||||
return getDnsRefreshRequestTime().orElse(null);
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getSubordinateHosts() {
|
||||
return nullToEmptyImmutableCopy(subordinateHosts);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.model.domain.fee;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.Period;
|
||||
import java.util.Optional;
|
||||
@@ -37,7 +39,19 @@ public abstract class FeeQueryCommandExtensionItem extends ImmutableObject {
|
||||
RENEW,
|
||||
TRANSFER,
|
||||
RESTORE,
|
||||
UPDATE
|
||||
UPDATE;
|
||||
|
||||
public static CommandName parseKnownCommand(String string) {
|
||||
try {
|
||||
CommandName command = valueOf(string);
|
||||
checkArgument(!command.equals(UNKNOWN));
|
||||
return command;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid EPP action name. Valid actions are CREATE, RENEW, TRANSFER, RESTORE, and"
|
||||
+ " UPDATE");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The default validity period (if not specified) is 1 year for all operations. */
|
||||
|
||||
@@ -45,6 +45,7 @@ import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.UpdateAutoTimestampEntity;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.WithVKey;
|
||||
@@ -213,6 +214,9 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
TimedTransitionProperty<TokenStatus> tokenStatusTransitions =
|
||||
TimedTransitionProperty.withInitialValue(NOT_STARTED);
|
||||
|
||||
/** Allowed EPP actions for this token, or null if all actions are allowed. */
|
||||
@Nullable Set<CommandName> allowedEppActions;
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
@@ -263,6 +267,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
return tokenStatusTransitions;
|
||||
}
|
||||
|
||||
public ImmutableSet<CommandName> getAllowedEppActions() {
|
||||
return nullToEmptyImmutableCopy(allowedEppActions);
|
||||
}
|
||||
|
||||
public RenewalPriceBehavior getRenewalPriceBehavior() {
|
||||
return renewalPriceBehavior;
|
||||
}
|
||||
@@ -450,6 +458,11 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAllowedEppActions(Set<CommandName> allowedEppActions) {
|
||||
getInstance().allowedEppActions = forceEmptyToNull(allowedEppActions);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPriceBehavior(RenewalPriceBehavior renewalPriceBehavior) {
|
||||
getInstance().renewalPriceBehavior = renewalPriceBehavior;
|
||||
return this;
|
||||
|
||||
@@ -49,7 +49,6 @@ import javax.persistence.Table;
|
||||
@Index(columnList = "creationTime"),
|
||||
@Index(columnList = "deletionTime"),
|
||||
@Index(columnList = "currentSponsorRegistrarId"),
|
||||
@Index(columnList = "dnsRefreshRequestTime")
|
||||
})
|
||||
@ExternalMessagingName("host")
|
||||
@WithVKey(String.class)
|
||||
@@ -94,7 +93,6 @@ public class Host extends HostBase implements ForeignKeyedEppResource {
|
||||
.setPersistedCurrentSponsorRegistrarId(hostBase.getPersistedCurrentSponsorRegistrarId())
|
||||
.setRepoId(hostBase.getRepoId())
|
||||
.setSuperordinateDomain(hostBase.getSuperordinateDomain())
|
||||
.setDnsRefreshRequestTime(hostBase.getDnsRefreshRequestTime())
|
||||
.setStatusValues(hostBase.getStatusValues());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -86,13 +85,6 @@ public class HostBase extends EppResource {
|
||||
*/
|
||||
DateTime lastSuperordinateChange;
|
||||
|
||||
@Access(AccessType.PROPERTY)
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "dnsRefreshRequestTime")
|
||||
private DateTime getInternalDnsRefreshRequestTime() {
|
||||
return getDnsRefreshRequestTime().orElse(null);
|
||||
}
|
||||
|
||||
public String getHostName() {
|
||||
return hostName;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import google.registry.util.RequestStatusCheckerImpl;
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
@@ -70,8 +68,7 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
enum LockState {
|
||||
IN_USE,
|
||||
FREE,
|
||||
TIMED_OUT,
|
||||
OWNER_DIED
|
||||
TIMED_OUT
|
||||
}
|
||||
|
||||
@VisibleForTesting static LockMetrics lockMetrics = new LockMetrics();
|
||||
@@ -79,17 +76,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
/** The name of the locked resource. */
|
||||
@Transient @Id String lockId;
|
||||
|
||||
/**
|
||||
* Unique log ID of the request that owns this lock.
|
||||
*
|
||||
* <p>When that request is no longer running (is finished), the lock can be considered implicitly
|
||||
* released.
|
||||
*
|
||||
* <p>See {@link RequestStatusCheckerImpl#getLogId} for details about how it's created in
|
||||
* practice.
|
||||
*/
|
||||
@Column String requestLogId;
|
||||
|
||||
/** When the lock can be considered implicitly released. */
|
||||
@Column(nullable = false)
|
||||
DateTime expirationTime;
|
||||
@@ -124,7 +110,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
private static Lock create(
|
||||
String resourceName,
|
||||
String scope,
|
||||
String requestLogId,
|
||||
DateTime acquiredTime,
|
||||
Duration leaseLength) {
|
||||
checkArgument(!Strings.isNullOrEmpty(resourceName), "resourceName cannot be null or empty");
|
||||
@@ -132,7 +117,6 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
// Add the scope to the Lock's id so that it is unique for locks acquiring the same resource
|
||||
// across different TLDs.
|
||||
instance.lockId = makeLockId(resourceName, scope);
|
||||
instance.requestLogId = requestLogId;
|
||||
instance.expirationTime = acquiredTime.plus(leaseLength);
|
||||
instance.acquiredTime = acquiredTime;
|
||||
instance.resourceName = resourceName;
|
||||
@@ -172,18 +156,13 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
switch (acquireResult.lockState()) {
|
||||
case IN_USE:
|
||||
logger.atInfo().log(
|
||||
"Existing lock by request %s is still valid now %s (until %s) lock: %s",
|
||||
lock.requestLogId, now, lock.expirationTime, lock.lockId);
|
||||
"Existing lock by request is still valid now %s (until %s) lock: %s",
|
||||
now, lock.expirationTime, lock.lockId);
|
||||
break;
|
||||
case TIMED_OUT:
|
||||
logger.atInfo().log(
|
||||
"Existing lock by request %s is timed out now %s (was valid until %s) lock: %s",
|
||||
lock.requestLogId, now, lock.expirationTime, lock.lockId);
|
||||
break;
|
||||
case OWNER_DIED:
|
||||
logger.atInfo().log(
|
||||
"Existing lock is valid now %s (until %s), but owner (%s) isn't running lock: %s",
|
||||
now, lock.expirationTime, lock.requestLogId, lock.lockId);
|
||||
"Existing lock by request is timed out now %s (was valid until %s) lock: %s",
|
||||
now, lock.expirationTime, lock.lockId);
|
||||
break;
|
||||
case FREE:
|
||||
// There was no existing lock
|
||||
@@ -203,11 +182,7 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
|
||||
/** Try to acquire a lock. Returns absent if it can't be acquired. */
|
||||
public static Optional<Lock> acquire(
|
||||
String resourceName,
|
||||
@Nullable String tld,
|
||||
Duration leaseLength,
|
||||
RequestStatusChecker requestStatusChecker,
|
||||
boolean checkThreadRunning) {
|
||||
String resourceName, @Nullable String tld, Duration leaseLength) {
|
||||
String scope = tld != null ? tld : GLOBAL;
|
||||
Supplier<AcquireResult> lockAcquirer =
|
||||
() -> {
|
||||
@@ -219,22 +194,19 @@ public class Lock extends ImmutableObject implements Serializable {
|
||||
.orElse(null);
|
||||
if (lock != null) {
|
||||
logger.atInfo().log(
|
||||
"Loaded existing lock: %s for request: %s", lock.lockId, lock.requestLogId);
|
||||
"Loaded existing lock: %s for resource: %s", lock.lockId, lock.resourceName);
|
||||
}
|
||||
LockState lockState;
|
||||
if (lock == null) {
|
||||
lockState = LockState.FREE;
|
||||
} else if (isAtOrAfter(now, lock.expirationTime)) {
|
||||
lockState = LockState.TIMED_OUT;
|
||||
} else if (checkThreadRunning && !requestStatusChecker.isRunning(lock.requestLogId)) {
|
||||
lockState = LockState.OWNER_DIED;
|
||||
} else {
|
||||
lockState = LockState.IN_USE;
|
||||
return AcquireResult.create(now, lock, null, lockState);
|
||||
}
|
||||
|
||||
Lock newLock =
|
||||
create(resourceName, scope, requestStatusChecker.getLogId(), now, leaseLength);
|
||||
Lock newLock = create(resourceName, scope, now, leaseLength);
|
||||
tm().put(newLock);
|
||||
|
||||
return AcquireResult.create(now, lock, newLock, lockState);
|
||||
|
||||
@@ -275,6 +275,26 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The time to live for DNS A and AAAA records.
|
||||
*
|
||||
* <p>When this field is null, the "dnsDefaultATtl" value from the config file will be used.
|
||||
*/
|
||||
Duration dnsAPlusAaaaTtl;
|
||||
|
||||
/**
|
||||
* The time to live for DNS NS records.
|
||||
*
|
||||
* <p>When this field is null, the "dnsDefaultNsTtl" value from the config file will be used.
|
||||
*/
|
||||
Duration dnsNsTtl;
|
||||
|
||||
/**
|
||||
* The time to live for DNS DS records.
|
||||
*
|
||||
* <p>When this field is null, the "dnsDefaultDsTtl" value from the config file will be used.
|
||||
*/
|
||||
Duration dnsDsTtl;
|
||||
/**
|
||||
* The unicode-aware representation of the TLD associated with this {@link Registry}.
|
||||
*
|
||||
@@ -647,6 +667,21 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return numDnsPublishLocks;
|
||||
}
|
||||
|
||||
/** Returns the time to live for A and AAAA records. */
|
||||
public Duration getDnsAPlusAaaaTtl() {
|
||||
return dnsAPlusAaaaTtl;
|
||||
}
|
||||
|
||||
/** Returns the time to live for NS records. */
|
||||
public Duration getDnsNsTtl() {
|
||||
return dnsNsTtl;
|
||||
}
|
||||
|
||||
/** Returns the time to live for DS records. */
|
||||
public Duration getDnsDsTtl() {
|
||||
return dnsDsTtl;
|
||||
}
|
||||
|
||||
public ImmutableSet<String> getAllowedRegistrantContactIds() {
|
||||
return nullToEmptyImmutableCopy(allowedRegistrantContactIds);
|
||||
}
|
||||
@@ -737,6 +772,21 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsAPlusAaaaTtl(Duration dnsAPlusAaaaTtl) {
|
||||
getInstance().dnsAPlusAaaaTtl = dnsAPlusAaaaTtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsNsAtl(Duration dnsNsAtl) {
|
||||
getInstance().dnsNsTtl = dnsNsAtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsDsAtl(Duration dnsDsAtl) {
|
||||
getInstance().dnsDsTtl = dnsDsAtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAddGracePeriodLength(Duration addGracePeriodLength) {
|
||||
checkArgument(
|
||||
addGracePeriodLength.isLongerThan(Duration.ZERO),
|
||||
|
||||
@@ -25,6 +25,7 @@ import google.registry.monitoring.whitebox.WhiteboxModule;
|
||||
import google.registry.request.RequestComponentBuilder;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.RequestScope;
|
||||
import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.registrar.ConsoleOteSetupAction;
|
||||
import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction;
|
||||
import google.registry.ui.server.registrar.ConsoleUiAction;
|
||||
@@ -61,6 +62,8 @@ interface FrontendRequestComponent {
|
||||
|
||||
RegistryLockVerifyAction registryLockVerifyAction();
|
||||
|
||||
ConsoleDomainGetAction consoleDomainGetAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<FrontendRequestComponent> {
|
||||
@Override public abstract Builder requestModule(RequestModule requestModule);
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
@@ -52,7 +53,7 @@ public class VKey<T> extends ImmutableObject implements Serializable {
|
||||
.collect(toImmutableMap(Class::getSimpleName, identity()));
|
||||
|
||||
// The primary key for the referenced entity.
|
||||
Serializable key;
|
||||
@Expose Serializable key;
|
||||
|
||||
Class<? extends T> kind;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2023 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.persistence.converter;
|
||||
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
@Converter(autoApply = true)
|
||||
public class CommandNameSetConverter
|
||||
extends StringSetConverterBase<FeeQueryCommandExtensionItem.CommandName> {
|
||||
|
||||
@Override
|
||||
String toString(CommandName element) {
|
||||
return element.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
CommandName fromString(String value) {
|
||||
return FeeQueryCommandExtensionItem.CommandName.valueOf(value);
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.SftpProgressMonitor;
|
||||
import dagger.Lazy;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
@@ -56,7 +57,6 @@ import google.registry.request.RequestParameters;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.TeeOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -131,8 +131,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
prefix.ifPresent(s -> params.put(RdeModule.PARAM_PREFIX, s));
|
||||
cloudTasksUtils.enqueue(
|
||||
RDE_REPORT_QUEUE,
|
||||
cloudTasksUtils.createPostTask(
|
||||
RdeReportAction.PATH, Service.BACKEND.getServiceId(), params));
|
||||
cloudTasksUtils.createPostTask(RdeReportAction.PATH, Service.BACKEND, params));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
@@ -37,7 +38,6 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
@@ -140,7 +140,7 @@ public class GenerateInvoicesAction implements Runnable {
|
||||
ReportingModule.BEAM_QUEUE,
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
PublishInvoicesAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.of(
|
||||
ReportingModule.PARAM_JOB_ID,
|
||||
jobId,
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.api.services.dataflow.model.Job;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.reporting.ReportingModule;
|
||||
import google.registry.request.Action;
|
||||
@@ -33,7 +34,6 @@ import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.YearMonth;
|
||||
@@ -127,7 +127,7 @@ public class PublishInvoicesAction implements Runnable {
|
||||
BillingModule.CRON_QUEUE,
|
||||
cloudTasksUtils.createPostTask(
|
||||
CopyDetailReportsAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.of(PARAM_YEAR_MONTH, yearMonth.toString())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.bigquery.BigqueryJobFailureException;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.reporting.icann.IcannReportingModule.ReportType;
|
||||
@@ -34,7 +35,6 @@ import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.SendEmailService;
|
||||
@@ -123,7 +123,7 @@ public final class IcannReportingStagingAction implements Runnable {
|
||||
CRON_QUEUE,
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
IcannReportingUploadAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
null,
|
||||
Duration.standardMinutes(2)));
|
||||
return null;
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
@@ -37,7 +38,6 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.io.IOException;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
@@ -135,7 +135,7 @@ public class GenerateSpec11ReportAction implements Runnable {
|
||||
ReportingModule.BEAM_QUEUE,
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
PublishSpec11ReportAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.of(
|
||||
ReportingModule.PARAM_JOB_ID,
|
||||
jobId,
|
||||
|
||||
@@ -39,8 +39,6 @@ import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.request.lock.LockHandlerImpl;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import google.registry.util.RequestStatusCheckerImpl;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -192,18 +190,6 @@ public final class RequestModule {
|
||||
return lockHandler;
|
||||
}
|
||||
|
||||
@Provides
|
||||
static RequestStatusChecker provideRequestStatusChecker(
|
||||
RequestStatusCheckerImpl requestStatusChecker) {
|
||||
return requestStatusChecker;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@RequestLogId
|
||||
static String provideRequestLogId(RequestStatusChecker requestStatusChecker) {
|
||||
return requestStatusChecker.getLogId();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JsonPayload
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
@@ -30,15 +31,26 @@ import javax.inject.Singleton;
|
||||
public class AuthModule {
|
||||
|
||||
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
|
||||
private static final String SA_ISSUER_URL = "https://accounts.google.com";
|
||||
|
||||
/** Provides the custom authentication mechanisms (including OAuth). */
|
||||
@Provides
|
||||
ImmutableList<AuthenticationMechanism> provideApiAuthenticationMechanisms(
|
||||
OAuthAuthenticationMechanism oauthAuthenticationMechanism,
|
||||
IapHeaderAuthenticationMechanism iapHeaderAuthenticationMechanism) {
|
||||
return ImmutableList.of(oauthAuthenticationMechanism, iapHeaderAuthenticationMechanism);
|
||||
IapHeaderAuthenticationMechanism iapHeaderAuthenticationMechanism,
|
||||
ServiceAccountAuthenticationMechanism serviceAccountAuthenticationMechanism) {
|
||||
return ImmutableList.of(
|
||||
oauthAuthenticationMechanism,
|
||||
iapHeaderAuthenticationMechanism,
|
||||
serviceAccountAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@interface IAP {}
|
||||
|
||||
@Qualifier
|
||||
@interface ServiceAccount {}
|
||||
|
||||
/** Provides the OAuthService instance. */
|
||||
@Provides
|
||||
OAuthService provideOauthService() {
|
||||
@@ -46,10 +58,18 @@ public class AuthModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IAP
|
||||
@Singleton
|
||||
TokenVerifier provideTokenVerifier(
|
||||
@Config("projectId") String projectId, @Config("projectIdNumber") long projectIdNumber) {
|
||||
String audience = String.format("/projects/%d/apps/%s", projectIdNumber, projectId);
|
||||
return TokenVerifier.newBuilder().setAudience(audience).setIssuer(IAP_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ServiceAccount
|
||||
@Singleton
|
||||
TokenVerifier provideServiceAccountTokenVerifier(@Config("projectId") String projectId) {
|
||||
return TokenVerifier.newBuilder().setAudience(projectId).setIssuer(SA_ISSUER_URL).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,11 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.request.auth.AuthModule.IAP;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@@ -37,41 +33,22 @@ import javax.servlet.http.HttpServletRequest;
|
||||
* @see <a href="https://cloud.google.com/iap/docs/signed-headers-howto">the documentation on GCP
|
||||
* IAP's signed headers for more information.</a>
|
||||
*/
|
||||
public class IapHeaderAuthenticationMechanism implements AuthenticationMechanism {
|
||||
public class IapHeaderAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private static final String ID_TOKEN_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the IAP-based authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static Optional<User> userForTesting = Optional.empty();
|
||||
|
||||
private final TokenVerifier tokenVerifier;
|
||||
|
||||
@Inject
|
||||
public IapHeaderAuthenticationMechanism(TokenVerifier tokenVerifier) {
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
public IapHeaderAuthenticationMechanism(@IAP TokenVerifier tokenVerifier) {
|
||||
super(tokenVerifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& userForTesting.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userForTesting.get()));
|
||||
}
|
||||
String rawIdToken = request.getHeader(ID_TOKEN_HEADER_NAME);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (TokenVerifier.VerificationException e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String emailAddress = (String) token.getPayload().get("email");
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
return request.getHeader(ID_TOKEN_HEADER_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
Optional<User> maybeUser = UserDao.loadUser(emailAddress);
|
||||
if (!maybeUser.isPresent()) {
|
||||
logger.atInfo().log("No user found for email address %s", emailAddress);
|
||||
@@ -80,8 +57,4 @@ public class IapHeaderAuthenticationMechanism implements AuthenticationMechanism
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setUserAuthInfoForTestServer(@Nullable User user) {
|
||||
userForTesting = Optional.ofNullable(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2022 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.request.auth;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.console.User;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public abstract class IdTokenAuthenticationBase implements AuthenticationMechanism {
|
||||
|
||||
public static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the IAP-based authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static Optional<User> userForTesting = Optional.empty();
|
||||
|
||||
private final TokenVerifier tokenVerifier;
|
||||
|
||||
public IdTokenAuthenticationBase(TokenVerifier tokenVerifier) {
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
}
|
||||
|
||||
abstract String rawTokenFromRequest(HttpServletRequest request);
|
||||
|
||||
abstract AuthResult authResultFromEmail(String email);
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& userForTesting.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userForTesting.get()));
|
||||
}
|
||||
String rawIdToken = rawTokenFromRequest(request);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String emailAddress = (String) token.getPayload().get("email");
|
||||
return authResultFromEmail(emailAddress);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setUserAuthInfoForTestServer(@Nullable User user) {
|
||||
userForTesting = Optional.ofNullable(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2023 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.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static google.registry.request.auth.AuthLevel.APP;
|
||||
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.auth.AuthModule.ServiceAccount;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A way to authenticate HTTP requests signed by Service Account
|
||||
*
|
||||
* <p>Currently used by cloud scheduler service account
|
||||
*/
|
||||
public class ServiceAccountAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
private final ImmutableList<String> serviceAccountEmails;
|
||||
|
||||
@Inject
|
||||
public ServiceAccountAuthenticationMechanism(
|
||||
@ServiceAccount TokenVerifier tokenVerifier,
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails) {
|
||||
super(tokenVerifier);
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
}
|
||||
|
||||
@Override
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
String rawToken = request.getHeader(AUTHORIZATION);
|
||||
if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) {
|
||||
return rawToken.substring(BEARER_PREFIX.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(emailAddress))) {
|
||||
return AuthResult.create(APP);
|
||||
} else {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import google.registry.model.server.Lock;
|
||||
import google.registry.util.AppEngineTimeLimiter;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.RequestStatusChecker;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -48,12 +47,10 @@ public class LockHandlerImpl implements LockHandler {
|
||||
/** Fudge factor to make sure we kill threads before a lock actually expires. */
|
||||
private static final Duration LOCK_TIMEOUT_FUDGE = Duration.standardSeconds(5);
|
||||
|
||||
private final RequestStatusChecker requestStatusChecker;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
public LockHandlerImpl(RequestStatusChecker requestStatusChecker, Clock clock) {
|
||||
this.requestStatusChecker = requestStatusChecker;
|
||||
public LockHandlerImpl(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@@ -114,7 +111,7 @@ public class LockHandlerImpl implements LockHandler {
|
||||
/** Allows injection of mock Lock in tests. */
|
||||
@VisibleForTesting
|
||||
Optional<Lock> acquire(String lockName, @Nullable String tld, Duration leaseLength) {
|
||||
return Lock.acquire(lockName, tld, leaseLength, requestStatusChecker, true);
|
||||
return Lock.acquire(lockName, tld, leaseLength);
|
||||
}
|
||||
|
||||
private interface LockAcquirer {
|
||||
|
||||
@@ -49,7 +49,6 @@ U+011F # LATIN SMALL LETTER G WITH BREVE
|
||||
U+01E7 # LATIN SMALL LETTER G WITH CARON
|
||||
U+0121 # LATIN SMALL LETTER G WITH DOT ABOVE
|
||||
U+0123 # LATIN SMALL LETTER G WITH CEDILLA
|
||||
U+01E5 # LATIN SMALL LETTER G WITH STROKE
|
||||
U+0068 # LATIN SMALL LETTER H
|
||||
U+0127 # LATIN SMALL LETTER H WITH STROKE
|
||||
U+0069 # LATIN SMALL LETTER I
|
||||
|
||||
@@ -14,21 +14,15 @@
|
||||
|
||||
package google.registry.tmch;
|
||||
|
||||
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.appengine.api.taskqueue.TaskOptions;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions.Method;
|
||||
import com.google.common.base.Joiner;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Helper methods for creating tasks containing CSV line data in the lordn-sunrise and lordn-claims
|
||||
* queues based on {@link Domain} changes.
|
||||
* Helper methods for creating tasks containing CSV line data based on {@link Domain#getLordnPhase}.
|
||||
*
|
||||
* <p>Note that, per the <a href="https://tools.ietf.org/html/draft-ietf-regext-tmch-func-spec-04">
|
||||
* TMCH RFC</a>, while the application-datetime data is optional (which we never send because there
|
||||
@@ -36,55 +30,32 @@ import org.joda.time.DateTime;
|
||||
*/
|
||||
public final class LordnTaskUtils {
|
||||
|
||||
public static final String QUEUE_SUNRISE = "lordn-sunrise";
|
||||
public static final String QUEUE_CLAIMS = "lordn-claims";
|
||||
public static final String COLUMNS_CLAIMS =
|
||||
"roid,domain-name,notice-id,registrar-id,"
|
||||
+ "registration-datetime,ack-datetime,application-datetime";
|
||||
public static final String COLUMNS_SUNRISE =
|
||||
"roid,domain-name,SMD-id,registrar-id," + "registration-datetime,application-datetime";
|
||||
|
||||
/** Enqueues a task in the LORDN queue representing a line of CSV for LORDN export. */
|
||||
public static void enqueueDomainTask(Domain domain) {
|
||||
tm().assertInTransaction();
|
||||
// This method needs to use transactionTime as the Domain's creationTime because CreationTime
|
||||
// isn't yet populated when this method is called during the resource flow.
|
||||
String tld = domain.getTld();
|
||||
if (domain.getLaunchNotice() == null) {
|
||||
getQueue(QUEUE_SUNRISE)
|
||||
.add(
|
||||
TaskOptions.Builder.withTag(tld)
|
||||
.method(Method.PULL)
|
||||
.payload(getCsvLineForSunriseDomain(domain, tm().getTransactionTime())));
|
||||
} else {
|
||||
getQueue(QUEUE_CLAIMS)
|
||||
.add(
|
||||
TaskOptions.Builder.withTag(tld)
|
||||
.method(Method.PULL)
|
||||
.payload(getCsvLineForClaimsDomain(domain, tm().getTransactionTime())));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the corresponding CSV LORDN line for a sunrise domain. */
|
||||
public static String getCsvLineForSunriseDomain(Domain domain, DateTime transactionTime) {
|
||||
public static String getCsvLineForSunriseDomain(Domain domain) {
|
||||
return Joiner.on(',')
|
||||
.join(
|
||||
domain.getRepoId(),
|
||||
domain.getDomainName(),
|
||||
domain.getSmdId(),
|
||||
getIanaIdentifier(domain.getCreationRegistrarId()),
|
||||
transactionTime); // Used as creation time.
|
||||
domain.getCreationTime()); // Used as creation time.
|
||||
}
|
||||
|
||||
/** Returns the corresponding CSV LORDN line for a claims domain. */
|
||||
public static String getCsvLineForClaimsDomain(Domain domain, DateTime transactionTime) {
|
||||
public static String getCsvLineForClaimsDomain(Domain domain) {
|
||||
return Joiner.on(',')
|
||||
.join(
|
||||
domain.getRepoId(),
|
||||
domain.getDomainName(),
|
||||
domain.getLaunchNotice().getNoticeId().getTcnId(),
|
||||
getIanaIdentifier(domain.getCreationRegistrarId()),
|
||||
transactionTime, // Used as creation time.
|
||||
domain.getCreationTime(), // Used as creation time.
|
||||
domain.getLaunchNotice().getAcceptedTime());
|
||||
}
|
||||
|
||||
@@ -100,16 +71,8 @@ public final class LordnTaskUtils {
|
||||
private LordnTaskUtils() {}
|
||||
|
||||
public enum LordnPhase {
|
||||
SUNRISE(QUEUE_SUNRISE),
|
||||
|
||||
CLAIMS(QUEUE_CLAIMS),
|
||||
|
||||
NONE(null);
|
||||
|
||||
final String queue;
|
||||
|
||||
LordnPhase(String queue) {
|
||||
this.queue = queue;
|
||||
}
|
||||
SUNRISE,
|
||||
CLAIMS,
|
||||
NONE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,17 @@
|
||||
|
||||
package google.registry.tmch;
|
||||
|
||||
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.net.HttpHeaders.LOCATION;
|
||||
import static com.google.common.net.MediaType.CSV_UTF_8;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.UrlConnectionUtils.getResponseBytes;
|
||||
import static google.registry.tmch.LordnTaskUtils.COLUMNS_CLAIMS;
|
||||
import static google.registry.tmch.LordnTaskUtils.COLUMNS_SUNRISE;
|
||||
import static google.registry.tmch.LordnTaskUtils.getCsvLineForClaimsDomain;
|
||||
import static google.registry.tmch.LordnTaskUtils.getCsvLineForSunriseDomain;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED;
|
||||
@@ -33,15 +36,17 @@ import com.google.appengine.api.taskqueue.TaskHandle;
|
||||
import com.google.appengine.api.taskqueue.TransientFailureException;
|
||||
import com.google.apphosting.api.DeadlineExceededException;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
@@ -49,8 +54,8 @@ import google.registry.request.RequestParameters;
|
||||
import google.registry.request.UrlConnectionService;
|
||||
import google.registry.request.UrlConnectionUtils;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.UrlConnectionException;
|
||||
import java.io.IOException;
|
||||
@@ -81,9 +86,9 @@ import org.joda.time.Duration;
|
||||
public final class NordnUploadAction implements Runnable {
|
||||
|
||||
static final String PATH = "/_dr/task/nordnUpload";
|
||||
static final String LORDN_PHASE_PARAM = "lordn-phase";
|
||||
static final String LORDN_PHASE_PARAM = "lordnPhase";
|
||||
|
||||
private static final int QUEUE_BATCH_SIZE = 1000;
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Duration LEASE_PERIOD = Duration.standardHours(1);
|
||||
|
||||
@@ -100,32 +105,91 @@ public final class NordnUploadAction implements Runnable {
|
||||
@Inject LordnRequestInitializer lordnRequestInitializer;
|
||||
@Inject UrlConnectionService urlConnectionService;
|
||||
|
||||
@Inject @Config("tmchMarksdbUrl") String tmchMarksdbUrl;
|
||||
@Inject @Parameter(LORDN_PHASE_PARAM) String phase;
|
||||
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
||||
@Inject
|
||||
@Config("tmchMarksdbUrl")
|
||||
String tmchMarksdbUrl;
|
||||
|
||||
@Inject
|
||||
@Parameter(LORDN_PHASE_PARAM)
|
||||
String phase;
|
||||
|
||||
@Inject
|
||||
@Parameter(RequestParameters.PARAM_TLD)
|
||||
String tld;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject NordnUploadAction() {}
|
||||
@Inject
|
||||
NordnUploadAction() {}
|
||||
|
||||
/**
|
||||
* These LORDN parameter names correspond to the relative paths in LORDN URLs and cannot be
|
||||
* changed on our end.
|
||||
*/
|
||||
private static final String PARAM_LORDN_PHASE_SUNRISE = "sunrise";
|
||||
private static final String PARAM_LORDN_PHASE_SUNRISE =
|
||||
Ascii.toLowerCase(LordnPhase.SUNRISE.toString());
|
||||
|
||||
private static final String PARAM_LORDN_PHASE_CLAIMS = "claims";
|
||||
private static final String PARAM_LORDN_PHASE_CLAIMS =
|
||||
Ascii.toLowerCase(LordnPhase.CLAIMS.toString());
|
||||
|
||||
/** How long to wait before attempting to verify an upload by fetching the log. */
|
||||
private static final Duration VERIFY_DELAY = Duration.standardMinutes(30);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
processLordnTasks();
|
||||
} catch (IOException | GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
checkArgument(
|
||||
phase.equals(PARAM_LORDN_PHASE_SUNRISE) || phase.equals(PARAM_LORDN_PHASE_CLAIMS),
|
||||
"Invalid phase specified to NordnUploadAction: %s.",
|
||||
phase);
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Note here that we load all domains pending Nordn in one batch, which should not
|
||||
// be a problem for the rate of domain registration that we see. If we anticipate
|
||||
// a peak in claims during TLD launch (sunrise is NOT first-come-first-serve, so
|
||||
// there should be no expectation of a peak during it), we can consider temporarily
|
||||
// increasing the frequency of Nordn upload to reduce the size of each batch.
|
||||
//
|
||||
// We did not further divide the domains into smaller batches because the
|
||||
// read-upload-write operation per small batch needs to be inside a single
|
||||
// transaction to prevent race conditions, and running several uploads in rapid
|
||||
// sucession will likely overwhelm the MarksDB upload server, which recommands a
|
||||
// maximum upload frequency of every 3 hours.
|
||||
//
|
||||
// See:
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-regext-tmch-func-spec-01#section-5.2.3.3
|
||||
List<Domain> domains =
|
||||
tm().createQueryComposer(Domain.class)
|
||||
.where("lordnPhase", EQ, LordnPhase.valueOf(Ascii.toUpperCase(phase)))
|
||||
.where("tld", EQ, tld)
|
||||
.orderBy("creationTime")
|
||||
.list();
|
||||
if (domains.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
StringBuilder csv = new StringBuilder();
|
||||
ImmutableList.Builder<Domain> newDomains = new ImmutableList.Builder<>();
|
||||
|
||||
domains.forEach(
|
||||
domain -> {
|
||||
if (phase.equals(PARAM_LORDN_PHASE_SUNRISE)) {
|
||||
csv.append(getCsvLineForSunriseDomain(domain)).append('\n');
|
||||
} else {
|
||||
csv.append(getCsvLineForClaimsDomain(domain)).append('\n');
|
||||
}
|
||||
Domain newDomain = domain.asBuilder().setLordnPhase(LordnPhase.NONE).build();
|
||||
newDomains.add(newDomain);
|
||||
});
|
||||
String columns =
|
||||
phase.equals(PARAM_LORDN_PHASE_SUNRISE) ? COLUMNS_SUNRISE : COLUMNS_CLAIMS;
|
||||
String header =
|
||||
String.format("1,%s,%d\n%s\n", clock.nowUtc(), domains.size(), columns);
|
||||
try {
|
||||
uploadCsvToLordn(String.format("/LORDN/%s/%s", tld, phase), header + csv);
|
||||
} catch (IOException | GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
tm().updateAll(newDomains.build());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +222,7 @@ public final class NordnUploadAction implements Runnable {
|
||||
queue.leaseTasks(
|
||||
LeaseOptions.Builder.withTag(tld)
|
||||
.leasePeriod(LEASE_PERIOD.getMillis(), TimeUnit.MILLISECONDS)
|
||||
.countLimit(QUEUE_BATCH_SIZE)),
|
||||
.countLimit(BATCH_SIZE)),
|
||||
TransientFailureException.class,
|
||||
DeadlineExceededException.class);
|
||||
if (tasks.isEmpty()) {
|
||||
@@ -168,35 +232,6 @@ public final class NordnUploadAction implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void processLordnTasks() throws IOException, GeneralSecurityException {
|
||||
checkArgument(
|
||||
phase.equals(PARAM_LORDN_PHASE_SUNRISE) || phase.equals(PARAM_LORDN_PHASE_CLAIMS),
|
||||
"Invalid phase specified to Nordn servlet: %s.",
|
||||
phase);
|
||||
DateTime now = clock.nowUtc();
|
||||
Queue queue =
|
||||
getQueue(
|
||||
phase.equals(PARAM_LORDN_PHASE_SUNRISE)
|
||||
? LordnTaskUtils.QUEUE_SUNRISE
|
||||
: LordnTaskUtils.QUEUE_CLAIMS);
|
||||
String columns = phase.equals(PARAM_LORDN_PHASE_SUNRISE) ? COLUMNS_SUNRISE : COLUMNS_CLAIMS;
|
||||
List<TaskHandle> tasks = loadAllTasks(queue, tld);
|
||||
// Note: This upload/task deletion isn't done atomically (it's not clear how one would do so
|
||||
// anyway). As a result, it is possible that the upload might succeed yet the deletion of
|
||||
// enqueued tasks might fail. If so, this would result in the same lines being uploaded to NORDN
|
||||
// across mulitple uploads. This is probably OK; all that we really cannot have is a missing
|
||||
// line.
|
||||
if (!tasks.isEmpty()) {
|
||||
String csvData = convertTasksToCsv(tasks, now, columns);
|
||||
uploadCsvToLordn(String.format("/LORDN/%s/%s", tld, phase), csvData);
|
||||
Lists.partition(tasks, QUEUE_BATCH_SIZE)
|
||||
.forEach(
|
||||
batch ->
|
||||
retrier.callWithRetry(
|
||||
() -> queue.deleteTask(batch), TransientFailureException.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload LORDN file to MarksDB.
|
||||
*
|
||||
@@ -252,7 +287,7 @@ public final class NordnUploadAction implements Runnable {
|
||||
// The actionLogId is used to uniquely associate the verify task back to the upload task.
|
||||
return cloudTasksUtils.createPostTaskWithDelay(
|
||||
NordnVerifyAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.<String, String>builder()
|
||||
.put(NordnVerifyAction.NORDN_URL_PARAM, url.toString())
|
||||
.put(NordnVerifyAction.NORDN_LOG_ID_PARAM, actionLogId)
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
|
||||
import google.registry.model.CacheUtils;
|
||||
@@ -55,6 +56,7 @@ public final class TmchCertificateAuthority {
|
||||
private static final String ROOT_CRT_PILOT_FILE = "icann-tmch-pilot.crt";
|
||||
private static final String CRL_FILE = "icann-tmch.crl";
|
||||
private static final String CRL_PILOT_FILE = "icann-tmch-pilot.crl";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final TmchCaMode tmchCaMode;
|
||||
private final Clock clock;
|
||||
@@ -142,8 +144,14 @@ public final class TmchCertificateAuthority {
|
||||
* @see X509Utils#verifyCrl
|
||||
*/
|
||||
public void updateCrl(String asciiCrl, String url) throws GeneralSecurityException {
|
||||
X509CRL crl = X509Utils.loadCrl(asciiCrl);
|
||||
X509Utils.verifyCrl(getAndValidateRoot(), getCrl(), crl, clock.nowUtc().toDate());
|
||||
X509CRL newCrl = X509Utils.loadCrl(asciiCrl);
|
||||
X509CRL oldCrl = null;
|
||||
try {
|
||||
oldCrl = getCrl();
|
||||
} catch (Exception e) {
|
||||
logger.atWarning().withCause(e).log("Old CRL is invalid, ignored during CRL update.");
|
||||
}
|
||||
X509Utils.verifyCrl(getAndValidateRoot(), oldCrl, newCrl, clock.nowUtc().toDate());
|
||||
TmchCrl.set(asciiCrl, url);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,10 @@ import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing;
|
||||
/** Helper class for common data loaded from the jar and SQL at runtime. */
|
||||
public final class TmchData {
|
||||
|
||||
private static final String BEGIN_ENCODED_SMD = "-----BEGIN ENCODED SMD-----";
|
||||
private static final String END_ENCODED_SMD = "-----END ENCODED SMD-----";
|
||||
static final String BEGIN_ENCODED_SMD = "-----BEGIN ENCODED SMD-----";
|
||||
static final String END_ENCODED_SMD = "-----END ENCODED SMD-----";
|
||||
|
||||
private TmchData() {}
|
||||
|
||||
static PGPPublicKey loadPublicKey(ByteSource pgpPublicKeyFile) {
|
||||
try (InputStream input = pgpPublicKeyFile.openStream();
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEVjCCAz6gAwIBAgIgLrAbevoae52y3f6C2tB0Sn3p7XJm0T02FogxKCfNhXkw
|
||||
DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxPDA6BgNVBAoTM0ludGVybmV0
|
||||
IENvcnBvcmF0aW9uIGZvciBBc3NpZ25lZCBOYW1lcyBhbmQgTnVtYmVyczEvMC0G
|
||||
A1UEAxMmSUNBTk4gVHJhZGVtYXJrIENsZWFyaW5naG91c2UgUGlsb3QgQ0EwHhcN
|
||||
MTMwNjI2MDAwMDAwWhcNMjMwNjI1MjM1OTU5WjB8MQswCQYDVQQGEwJVUzE8MDoG
|
||||
A1UEChMzSW50ZXJuZXQgQ29ycG9yYXRpb24gZm9yIEFzc2lnbmVkIE5hbWVzIGFu
|
||||
ZCBOdW1iZXJzMS8wLQYDVQQDEyZJQ0FOTiBUcmFkZW1hcmsgQ2xlYXJpbmdob3Vz
|
||||
ZSBQaWxvdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMJiRqFg
|
||||
iCoDF8zMJMKHPMEuSpjbEl9ZWII+1WawDyt+jw841HsTT+6MwZsqExbQvukgvnuS
|
||||
lA3Rg3xTFxodMaVZWsVQJy2PXGHVFRLnCp05DYZsMGZabuN9mIekYwtjePo89Lz0
|
||||
JtU3ibL3squGG3gg6TLtPjks7Txm18BYPOYLznui32GUz+1aIZuk2p5A/rSldsh3
|
||||
bke68IX5WZhKuIxT0+BjS8yfLWI0HCUs71WVxzvlJ1v22/eMK0WEA6+ZhCbOKIav
|
||||
VtGNJrwIYwhZmxqfiR1HzHTLvrV0SLlJ2bwNk/yzKm8IJfuFezQ5BBtQ2RS9opFX
|
||||
X8ft3v+uQQQvi+MCAwEAAaOBwzCBwDASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud
|
||||
DgQWBBTDrT6m1hEARYBcOldKim3cMQ2ecTAOBgNVHQ8BAf8EBAMCAQYwNAYDVR0f
|
||||
BC0wKzApoCegJYYjaHR0cDovL2NybC5pY2Fubi5vcmcvdG1jaF9waWxvdC5jcmww
|
||||
RQYDVR0gBD4wPDA6BgMqAwQwMzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5pY2Fu
|
||||
bi5vcmcvcGlsb3RfcmVwb3NpdG9yeTANBgkqhkiG9w0BAQsFAAOCAQEAKUfEJ5X6
|
||||
QAttajjRVseJFQxRXGHTgCaDk8C/1nj1ielZAuZtgdUpWDUr0NnGCi+LHSsgdTYR
|
||||
+vMrxir7EVYQevrBobELkxeTEfjF9FVqjBHInyPFLOFkz15zGG2IwPJps+vhAd/7
|
||||
gT0ph1k2FEkJFGL5LwRf1ms4IX0vDkxTIX8Qxy1jczCiSsoV8pwlhh2NHAkpGQWN
|
||||
/pTS0Uqi7uU5Bm/IoGvPBzUp5n5SjUMnTZx/+1zAuerSabt483sXBcWsjgl7MqFt
|
||||
fONiAtNeMNfh60lTMu4zgVwLZTO4TQM5Q2uylPPmZtwnA88QvM2IL85cIYJHd0z9
|
||||
jpUQMBGHXF2WQA==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,13 +0,0 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIB8DCB2QIBATANBgkqhkiG9w0BAQsFADB2MQswCQYDVQQGEwJVUzE8MDoGA1UE
|
||||
ChMzSW50ZXJuZXQgQ29ycG9yYXRpb24gZm9yIEFzc2lnbmVkIE5hbWVzIGFuZCBO
|
||||
dW1iZXJzMSkwJwYDVQQDEyBJQ0FOTiBUcmFkZW1hcmsgQ2xlYXJpbmdob3VzZSBD
|
||||
QRcNMTgwMzAxMDAwMDAwWhcNMTgxMDA3MjM1OTU5WqAvMC0wHwYDVR0jBBgwFoAU
|
||||
XMDxlizKTFsp8UB00xs2PkfUbgQwCgYDVR0UBAMCAQswDQYJKoZIhvcNAQELBQAD
|
||||
ggEBAGhvQtqENy2Ga+nGg6kZRCzEWKy481v111Iycku/qL5aUlqSL5BkQst2Czaq
|
||||
xdKRSxKHkMaTChoezSaw5huOTd0prdSXVHPg/tmjxyuuS2pqWpuAICkrG06FgXgh
|
||||
AG5YCHt2DvCjeA9F3TMmbOkCMILQ/x+vsyg6Yv4Oiz8rFbFcUMntUKSrymt4dKpk
|
||||
S78CTkHH/3M3YNxZCo8JPwaQohC3Rck4M30Pg8C0qC9jjSrudA6hCa4223U6aZwC
|
||||
Kz3LNXdkqGWlDJPTf0YWwnT4ZyO7KKXVuEbPzg187htz3Jcr6b0x1UUoHGAkOv7i
|
||||
W4IwhPbUJ14/7pUuUef6airQUw8=
|
||||
-----END X509 CRL-----
|
||||
@@ -1,25 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEQjCCAyqgAwIBAgIhAJNCMqhNjz3cXVJPj7yvcZvro1FKQR+dTC6tXazem5g+
|
||||
MA0GCSqGSIb3DQEBCwUAMHYxCzAJBgNVBAYTAlVTMTwwOgYDVQQKEzNJbnRlcm5l
|
||||
dCBDb3Jwb3JhdGlvbiBmb3IgQXNzaWduZWQgTmFtZXMgYW5kIE51bWJlcnMxKTAn
|
||||
BgNVBAMTIElDQU5OIFRyYWRlbWFyayBDbGVhcmluZ2hvdXNlIENBMB4XDTEzMDcy
|
||||
NDAwMDAwMFoXDTIzMDcyMzIzNTk1OVowdjELMAkGA1UEBhMCVVMxPDA6BgNVBAoT
|
||||
M0ludGVybmV0IENvcnBvcmF0aW9uIGZvciBBc3NpZ25lZCBOYW1lcyBhbmQgTnVt
|
||||
YmVyczEpMCcGA1UEAxMgSUNBTk4gVHJhZGVtYXJrIENsZWFyaW5naG91c2UgQ0Ew
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5MX6qpRnqFzEXa9w3G0b8
|
||||
LTEVZzpOpcSq2BXJO16+iuZ964mpay2hm2BdZk89hSmZhUy2ePBR6PdS0GMmzzXL
|
||||
NiyTHJlDIPxxXTR39Iqs8QChJ8wle4pYUu8JUk2vJ0r7PhFweeCCQZ5gvHdCwopS
|
||||
bXeolj4NCqsvzU8iROsLRHSZbE83i2pkL+qBoyzjny9MO2rvMNPo5WrDNrno6hvC
|
||||
hlf8Pv77HTNCazI2MeW0ArfLin4pSe6nLnDsQA11SF9bbgwDgVMQFvmB8nEvUbZW
|
||||
Atnp3auaWqaylC+G0p3frFvMCUJMPrghiPwBABl3bk1GLjXXVl7D8SubKd2Xwv63
|
||||
AgMBAAGjgbowgbcwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUXMDxlizK
|
||||
TFsp8UB00xs2PkfUbgQwDgYDVR0PAQH/BAQDAgEGMC4GA1UdHwQnMCUwI6AhoB+G
|
||||
HWh0dHA6Ly9jcmwuaWNhbm4ub3JnL3RtY2guY3JsMEIGA1UdIAQ7MDkwBgYEVR0g
|
||||
ADAvBggrBgEFBQcCATAjMCEGCCsGAQUFBwIBFhVodHRwczovL2NhLmljYW5uLm9y
|
||||
Zy8wDQYJKoZIhvcNAQELBQADggEBAAM29FBdwQSAx8dD4ZYtCYjXxTonNCP2qveG
|
||||
wrpMJcq/I3Jp/N4etsnj+K5ej5sSlDuo8sTMF7lgMkgjrc6zgJl0+Gct2RhbRNzN
|
||||
5ittE9JwJZ3Us4vwiy6gqMO5Ie9YaKMZy2MYP2iFp6AhBKIc2Iz+8aFfnFzdSEx2
|
||||
b3xc+t1A09dzpnzU6zvHWUUkTYq9fTg1er1npni9ZErvp0jEyHVWi5GXvWap68XH
|
||||
pVF6TBmPW2UBEEnFgd3SxbMXLhaD/wzV12tlSYjxaNed+H5qbVVbSVwN9yBeWU29
|
||||
/pkZu79TqFFxTd4CJTWOBq0+yLO1Ts2ZZ1l+GgU6e3hI1XERSNc=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.tools.params.KeyValueMapParameter.StringToRegistrarRoleMap;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Shared base class for commands that create or modify a {@link User}. */
|
||||
public abstract class CreateOrUpdateUserCommand extends ConfirmingCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(names = "--email", description = "Email address of the user", required = true)
|
||||
String email;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--admin",
|
||||
description = "Whether or not the user in question is an admin",
|
||||
arity = 1)
|
||||
private Boolean isAdmin;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--global_role",
|
||||
description = "Global role, e.g. SUPPORT_LEAD, to apply to the user")
|
||||
private GlobalRole globalRole;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--registrar_roles",
|
||||
converter = StringToRegistrarRoleMap.class,
|
||||
validateWith = StringToRegistrarRoleMap.class,
|
||||
description =
|
||||
"Comma-delimited mapping of registrar name to role that the user has on that registrar")
|
||||
private ImmutableMap<String, RegistrarRole> registrarRolesMap;
|
||||
|
||||
@Nullable
|
||||
abstract User getExistingUser(String email);
|
||||
|
||||
@Override
|
||||
protected final String execute() throws Exception {
|
||||
checkArgumentNotNull(email, "Email must be provided");
|
||||
tm().transact(this::executeInTransaction);
|
||||
return String.format("Saved user with email %s", email);
|
||||
}
|
||||
|
||||
private void executeInTransaction() {
|
||||
User user = getExistingUser(email);
|
||||
UserRoles.Builder userRolesBuilder =
|
||||
(user == null) ? new UserRoles.Builder() : user.getUserRoles().asBuilder();
|
||||
|
||||
Optional.ofNullable(globalRole).ifPresent(userRolesBuilder::setGlobalRole);
|
||||
Optional.ofNullable(registrarRolesMap).ifPresent(userRolesBuilder::setRegistrarRoles);
|
||||
Optional.ofNullable(isAdmin).ifPresent(userRolesBuilder::setIsAdmin);
|
||||
|
||||
User.Builder builder =
|
||||
(user == null) ? new User.Builder().setEmailAddress(email) : user.asBuilder();
|
||||
builder.setUserRoles(userRolesBuilder.build());
|
||||
User newUser = builder.build();
|
||||
UserDao.saveUser(newUser);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command to create a new User. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a user account")
|
||||
public class CreateUserCommand extends CreateOrUpdateUserCommand {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
User getExistingUser(String email) {
|
||||
checkArgument(
|
||||
!UserDao.loadUser(email).isPresent(), "A user with email %s already exists", email);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Deletes a {@link User}. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete a user account")
|
||||
public class DeleteUserCommand extends ConfirmingCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(names = "--email", description = "Email address of the user", required = true)
|
||||
String email;
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
checkArgumentNotNull(email, "Email must be provided");
|
||||
checkArgumentPresent(UserDao.loadUser(email), "Email does not correspond to a valid user");
|
||||
return String.format("Delete user with email %s?", email);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
tm().transact(
|
||||
() -> {
|
||||
Optional<User> optionalUser = UserDao.loadUser(email);
|
||||
checkArgumentPresent(optionalUser, "Email no longer corresponds to a valid user");
|
||||
tm().delete(optionalUser.get());
|
||||
});
|
||||
return String.format("Deleted user with email %s", email);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STAT
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
@@ -34,7 +35,6 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.RegistryLockDao;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -223,7 +223,7 @@ public final class DomainLockUtils {
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
RelockDomainAction.PATH,
|
||||
Service.BACKEND.toString(),
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.of(
|
||||
RelockDomainAction.OLD_UNLOCK_REVISION_ID_PARAM,
|
||||
String.valueOf(lockRevisionId),
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.io.Files;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
@@ -114,6 +115,11 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
description = "Comma-separated list of allowed TLDs, or null if all are allowed")
|
||||
private List<String> allowedTlds;
|
||||
|
||||
@Parameter(
|
||||
names = {"--allowed_epp_actions"},
|
||||
description = "Comma-separated list of allowed EPP actions, or null if all are allowed")
|
||||
private List<String> allowedEppActions;
|
||||
|
||||
@Parameter(
|
||||
names = {"--discount_fraction"},
|
||||
description =
|
||||
@@ -207,7 +213,13 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
.setTokenType(tokenType == null ? SINGLE_USE : tokenType)
|
||||
.setAllowedRegistrarIds(
|
||||
ImmutableSet.copyOf(nullToEmpty(allowedClientIds)))
|
||||
.setAllowedTlds(ImmutableSet.copyOf(nullToEmpty(allowedTlds)));
|
||||
.setAllowedTlds(ImmutableSet.copyOf(nullToEmpty(allowedTlds)))
|
||||
.setAllowedEppActions(
|
||||
isNullOrEmpty(allowedEppActions)
|
||||
? ImmutableSet.of()
|
||||
: allowedEppActions.stream()
|
||||
.map(CommandName::parseKnownCommand)
|
||||
.collect(toImmutableSet()));
|
||||
Optional.ofNullable(discountFraction).ifPresent(token::setDiscountFraction);
|
||||
Optional.ofNullable(discountPremiums).ifPresent(token::setDiscountPremiums);
|
||||
Optional.ofNullable(discountYears).ifPresent(token::setDiscountYears);
|
||||
@@ -255,6 +267,10 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
!ImmutableList.of("").equals(allowedTlds),
|
||||
"Either omit --allowed_tlds if all TLDs are allowed, or include a comma-separated list");
|
||||
|
||||
if (ImmutableList.of("").equals(allowedEppActions)) {
|
||||
allowedEppActions = ImmutableList.of();
|
||||
}
|
||||
|
||||
if (!isNullOrEmpty(tokenStatusTransitions)) {
|
||||
// Don't allow package tokens to be created with a scheduled end time since this could allow
|
||||
// future domains to be attributed to the package and never be billed. Package promotion
|
||||
|
||||
@@ -28,11 +28,11 @@ import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.rde.RdeStagingAction;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.tools.params.DateTimeParameter;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
@@ -122,7 +122,7 @@ final class GenerateEscrowDepositCommand implements Command {
|
||||
cloudTasksUtils.enqueue(
|
||||
RDE_REPORT_QUEUE,
|
||||
cloudTasksUtils.createPostTask(
|
||||
RdeStagingAction.PATH, Service.BACKEND.toString(), paramsBuilder.build()));
|
||||
RdeStagingAction.PATH, Service.BACKEND, paramsBuilder.build()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -94,10 +94,10 @@ final class GenerateLordnCommand implements Command {
|
||||
Domain domain) {
|
||||
String status = " ";
|
||||
if (domain.getLaunchNotice() == null && domain.getSmdId() != null) {
|
||||
sunriseCsv.add(LordnTaskUtils.getCsvLineForSunriseDomain(domain, domain.getCreationTime()));
|
||||
sunriseCsv.add(LordnTaskUtils.getCsvLineForSunriseDomain(domain));
|
||||
status = "S";
|
||||
} else if (domain.getLaunchNotice() != null || domain.getSmdId() != null) {
|
||||
claimsCsv.add(LordnTaskUtils.getCsvLineForClaimsDomain(domain, domain.getCreationTime()));
|
||||
claimsCsv.add(LordnTaskUtils.getCsvLineForClaimsDomain(domain));
|
||||
status = "C";
|
||||
}
|
||||
System.out.printf("%s[%s] ", domain.getDomainName(), status);
|
||||
|
||||
@@ -46,6 +46,7 @@ public final class RegistryTool {
|
||||
.put("create_registrar_groups", CreateRegistrarGroupsCommand.class)
|
||||
.put("create_reserved_list", CreateReservedListCommand.class)
|
||||
.put("create_tld", CreateTldCommand.class)
|
||||
.put("create_user", CreateUserCommand.class)
|
||||
.put("curl", CurlCommand.class)
|
||||
.put("delete_allocation_tokens", DeleteAllocationTokensCommand.class)
|
||||
.put("delete_domain", DeleteDomainCommand.class)
|
||||
@@ -53,6 +54,7 @@ public final class RegistryTool {
|
||||
.put("delete_premium_list", DeletePremiumListCommand.class)
|
||||
.put("delete_reserved_list", DeleteReservedListCommand.class)
|
||||
.put("delete_tld", DeleteTldCommand.class)
|
||||
.put("delete_user", DeleteUserCommand.class)
|
||||
.put("encrypt_escrow_deposit", EncryptEscrowDepositCommand.class)
|
||||
.put("enqueue_poll_message", EnqueuePollMessageCommand.class)
|
||||
.put("execute_epp", ExecuteEppCommand.class)
|
||||
@@ -110,6 +112,7 @@ public final class RegistryTool {
|
||||
.put("update_reserved_list", UpdateReservedListCommand.class)
|
||||
.put("update_server_locks", UpdateServerLocksCommand.class)
|
||||
.put("update_tld", UpdateTldCommand.class)
|
||||
.put("update_user", UpdateUserCommand.class)
|
||||
.put("upload_claims_list", UploadClaimsListCommand.class)
|
||||
.put("validate_escrow_deposit", ValidateEscrowDepositCommand.class)
|
||||
.put("validate_login_credentials", ValidateLoginCredentialsCommand.class)
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
@@ -63,6 +64,13 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
+ "existing list.")
|
||||
private List<String> allowedTlds;
|
||||
|
||||
@Parameter(
|
||||
names = {"--allowed_epp_actions"},
|
||||
description =
|
||||
"Comma-separated list of allowed EPP actions. Use an empty string to clear the existing"
|
||||
+ " list.")
|
||||
private List<String> allowedEppActions;
|
||||
|
||||
@Parameter(
|
||||
names = {"--discount_fraction"},
|
||||
description =
|
||||
@@ -128,6 +136,9 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
if (ImmutableList.of("").equals(allowedTlds)) {
|
||||
allowedTlds = ImmutableList.of();
|
||||
}
|
||||
if (ImmutableList.of("").equals(allowedEppActions)) {
|
||||
allowedEppActions = ImmutableList.of();
|
||||
}
|
||||
|
||||
if (tokenStatusTransitions != null
|
||||
&& (tokenStatusTransitions.containsValue(TokenStatus.ENDED)
|
||||
@@ -184,6 +195,14 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
.ifPresent(clientIds -> builder.setAllowedRegistrarIds(ImmutableSet.copyOf(clientIds)));
|
||||
Optional.ofNullable(allowedTlds)
|
||||
.ifPresent(tlds -> builder.setAllowedTlds(ImmutableSet.copyOf(tlds)));
|
||||
Optional.ofNullable(allowedEppActions)
|
||||
.ifPresent(
|
||||
eppActions -> {
|
||||
builder.setAllowedEppActions(
|
||||
eppActions.stream()
|
||||
.map(CommandName::parseKnownCommand)
|
||||
.collect(toImmutableSet()));
|
||||
});
|
||||
Optional.ofNullable(discountFraction).ifPresent(builder::setDiscountFraction);
|
||||
Optional.ofNullable(discountPremiums).ifPresent(builder::setDiscountPremiums);
|
||||
Optional.ofNullable(discountYears).ifPresent(builder::setDiscountYears);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Updates a user, assuming that the user in question already exists. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a user account")
|
||||
public class UpdateUserCommand extends CreateOrUpdateUserCommand {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
User getExistingUser(String email) {
|
||||
return checkArgumentPresent(UserDao.loadUser(email), "User %s not found", email);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package google.registry.tools.params;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import java.util.Map;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
@@ -107,4 +108,18 @@ public abstract class KeyValueMapParameter<K, V>
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Combined converter/validator class for maps of registrar names to registrar roles. */
|
||||
public static class StringToRegistrarRoleMap extends KeyValueMapParameter<String, RegistrarRole> {
|
||||
|
||||
@Override
|
||||
protected String parseKey(String rawKey) {
|
||||
return rawKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RegistrarRole parseValue(String rawValue) {
|
||||
return RegistrarRole.valueOf(rawValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
@@ -66,7 +66,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
@Parameter("smearMinutes")
|
||||
int smearMinutes;
|
||||
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject Clock clock;
|
||||
@Inject Random random;
|
||||
|
||||
@@ -91,7 +91,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
domainName -> {
|
||||
try {
|
||||
// Smear the task execution time over the next N minutes.
|
||||
dnsQueue.addDomainRefreshTask(
|
||||
dnsUtils.requestDomainDnsRefresh(
|
||||
domainName, Duration.standardMinutes(random.nextInt(smearMinutes)));
|
||||
} catch (Throwable t) {
|
||||
logger.atSevere().withCause(t).log(
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2023 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.ui.server.console;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Returns a JSON representation of a domain to the registrar console. */
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = ConsoleDomainGetAction.PATH,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleDomainGetAction implements JsonGetAction {
|
||||
|
||||
public static final String PATH = "/console-api/domain";
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final Gson gson;
|
||||
private final String paramDomain;
|
||||
|
||||
@Inject
|
||||
public ConsoleDomainGetAction(
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
Gson gson,
|
||||
@Parameter("domain") String paramDomain) {
|
||||
this.authResult = authResult;
|
||||
this.response = response;
|
||||
this.gson = gson;
|
||||
this.paramDomain = paramDomain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!authResult.isAuthenticated() || !authResult.userAuthInfo().isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
UserAuthInfo authInfo = authResult.userAuthInfo().get();
|
||||
if (!authInfo.consoleUser().isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
User user = authInfo.consoleUser().get();
|
||||
Optional<Domain> possibleDomain =
|
||||
tm().transact(
|
||||
() ->
|
||||
EppResourceUtils.loadByForeignKeyCached(
|
||||
Domain.class, paramDomain, tm().getTransactionTime()));
|
||||
if (!possibleDomain.isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
Domain domain = possibleDomain.get();
|
||||
if (!user.getUserRoles()
|
||||
.hasPermission(domain.getCurrentSponsorRegistrarId(), ConsolePermission.DOWNLOAD_DOMAINS)) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
response.setPayload(gson.toJson(domain));
|
||||
}
|
||||
}
|
||||
@@ -156,4 +156,10 @@ public final class RegistrarConsoleModule {
|
||||
static Boolean provideIsLock(HttpServletRequest req) {
|
||||
return extractBooleanParameter(req, "isLock");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("domain")
|
||||
static String provideDomain(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "domain");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.export.sheet.SyncRegistrarsSheetAction;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
@@ -58,7 +59,6 @@ import google.registry.ui.forms.FormException;
|
||||
import google.registry.ui.forms.FormFieldException;
|
||||
import google.registry.ui.server.RegistrarFormFields;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.util.CloudTasksUtils;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.DiffUtils;
|
||||
import java.util.HashSet;
|
||||
@@ -643,7 +643,7 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
||||
cloudTasksUtils.enqueue(
|
||||
SyncRegistrarsSheetAction.QUEUE,
|
||||
cloudTasksUtils.createGetTask(
|
||||
SyncRegistrarsSheetAction.PATH, Service.BACKEND.toString(), ImmutableMultimap.of()));
|
||||
SyncRegistrarsSheetAction.PATH, Service.BACKEND, ImmutableMultimap.of()));
|
||||
}
|
||||
String environment = Ascii.toLowerCase(String.valueOf(RegistryEnvironment.get()));
|
||||
sendEmailUtils.sendEmail(
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<class>google.registry.model.billing.BillingEvent$Recurring</class>
|
||||
<class>google.registry.model.common.Cursor</class>
|
||||
<class>google.registry.model.common.DatabaseMigrationStateSchedule</class>
|
||||
<class>google.registry.model.common.DnsRefreshRequest</class>
|
||||
<class>google.registry.model.console.User</class>
|
||||
<class>google.registry.model.contact.ContactHistory</class>
|
||||
<class>google.registry.model.contact.Contact</class>
|
||||
@@ -84,6 +85,7 @@
|
||||
<class>google.registry.persistence.converter.BillingEventFlagSetConverter</class>
|
||||
<class>google.registry.persistence.converter.BloomFilterConverter</class>
|
||||
<class>google.registry.persistence.converter.CidrAddressBlockListConverter</class>
|
||||
<class>google.registry.persistence.converter.CommandNameSetConverter</class>
|
||||
<class>google.registry.persistence.converter.CurrencyToBillingConverter</class>
|
||||
<class>google.registry.persistence.converter.CurrencyUnitConverter</class>
|
||||
<class>google.registry.persistence.converter.DatabaseMigrationScheduleTransitionConverter</class>
|
||||
|
||||
@@ -23,12 +23,6 @@
|
||||
"helpText": "The exclusive upper bound of the operation window, in ISO 8601 format.",
|
||||
"is_optional": false
|
||||
},
|
||||
{
|
||||
"name": "shard",
|
||||
"label": "The exclusive upper bound of the operation window.",
|
||||
"helpText": "The exclusive upper bound of the operation window, in ISO 8601 format.",
|
||||
"is_optional": true
|
||||
},
|
||||
{
|
||||
"name": "isDryRun",
|
||||
"label": "Whether this job is a dry run.",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-----BEGIN X509 CRL-----
|
||||
MIIDLjCCARYCAQEwDQYJKoZIhvcNAQENBQAwfDELMAkGA1UEBhMCVVMxPDA6BgNV
|
||||
BAoMM0ludGVybmV0IENvcnBvcmF0aW9uIGZvciBBc3NpZ25lZCBOYW1lcyBhbmQg
|
||||
TnVtYmVyczEvMC0GA1UEAwwmSUNBTk4gVHJhZGVtYXJrIENsZWFyaW5naG91c2Ug
|
||||
UGlsb3QgQ0EXDTIyMTExNjEzMzIyN1oXDTIzMDQwNjEzMzIyN1owNTAzAhQc4zug
|
||||
SmVXTpNkiBlOLRFSS6qBnhcNMjIxMTE2MTMzMjI3WjAMMAoGA1UdFQQDCgEAoC8w
|
||||
LTAfBgNVHSMEGDAWgBRHe0+uelGRtdG/nTbphn82FgSpRTAKBgNVHRQEAwIBATAN
|
||||
BgkqhkiG9w0BAQ0FAAOCAgEAD5pxGf7W2nxnCyoMhhRWclaBrY2VF+lYitpQdUIA
|
||||
Li3nhCXDiXwHWNtiyGjKaWJFsCFAph0EejKTR1iHh/7t7fZ5T2KhiqyREGGs2VT5
|
||||
AXTWTDZUdGtobmc9xmXowzF7YUWoZZenj1AwUt0/tDKth1ieZvoRWEKJ+0YCpIDQ
|
||||
LyfJKBFM6NEyrYiKu1szcdT7+Meii4M8ulYHkUXEJXQBN5+LuESkRrqach6n6zSS
|
||||
urvCG2I0ijqVj36TudwRrYU+sCpk8XR6//XyJbcjW5vuUd1/uYrJ+lSZCPaKfjIo
|
||||
gLKK1UJ9wLZbg28Hqtb5KxYsLtlzMsNFCbVebatl6QdS+J4OaCGLE4SM2NnsheN/
|
||||
qxErMS1eAzNVgpW+yY4JV47k54HDBZfgHmgA8lgyShn7w08dA2mWIYbmbKHcnh2N
|
||||
QSQ5wjYbTEy3a251t32pR76W5k/uduoMXestJEXD7yViMz3pFzu97l1huAP8C0Hs
|
||||
sMEysMomthMQChqNUZw19wakdxuCyiU+YrL7bHTTL2pAKYB0AKOGE1XgecnneC89
|
||||
+K5vr5TUTWOpksUeuM5Oq2YYLicYuES1Nr/5GkszNCCY0J+fBL50AI0IItrfFjt1
|
||||
Xu/mlbiJKQZXMppWfLJUhu/8Ur1VYeYLeCNR3ju7jfvm3DkVzmqSc0b01uz5POTz
|
||||
Hak=
|
||||
-----END X509 CRL-----
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user