mirror of
https://github.com/google/nomulus
synced 2026-05-17 13:21:48 +00:00
Compare commits
19 Commits
proxy-2023
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23fb69a682 | ||
|
|
597f63a603 | ||
|
|
5ec73f3809 | ||
|
|
b474e50e87 | ||
|
|
6f3d062c32 | ||
|
|
371d83b4cc | ||
|
|
e1f29a8103 | ||
|
|
055a52f67e | ||
|
|
d17678959c | ||
|
|
79ba1b94c4 | ||
|
|
33a771b13e | ||
|
|
bd65c6eee6 | ||
|
|
20c673840e | ||
|
|
11c60b8c8f | ||
|
|
e330fd1c66 | ||
|
|
57c17042b6 | ||
|
|
8623fce119 | ||
|
|
7243575433 | ||
|
|
8eab43d371 |
@@ -693,10 +693,6 @@ createToolTask(
|
||||
'google.registry.tools.DevTool',
|
||||
sourceSets.nonprod)
|
||||
|
||||
createToolTask(
|
||||
'createSyntheticDomainHistories',
|
||||
'google.registry.tools.javascrap.CreateSyntheticDomainHistoriesPipeline')
|
||||
|
||||
project.tasks.create('generateSqlSchema', JavaExec) {
|
||||
classpath = sourceSets.nonprod.runtimeClasspath
|
||||
main = 'google.registry.tools.DevTool'
|
||||
@@ -766,6 +762,11 @@ if (environment == 'alpha') {
|
||||
mainClass: 'google.registry.beam.resave.ResaveAllEppResourcesPipeline',
|
||||
metaData: 'google/registry/beam/resave_all_epp_resources_pipeline_metadata.json'
|
||||
],
|
||||
wipeOutContactHistoryPii:
|
||||
[
|
||||
mainClass: 'google.registry.beam.wipeout.WipeOutContactHistoryPiiPipeline',
|
||||
metaData: 'google/registry/beam/wipe_out_contact_history_pii_pipeline_metadata.json'
|
||||
],
|
||||
]
|
||||
project.tasks.create("stageBeamPipelines") {
|
||||
doLast {
|
||||
|
||||
@@ -117,6 +117,12 @@ public class BatchModule {
|
||||
return extractOptionalDatetimeParameter(req, ExpandRecurringBillingEventsAction.PARAM_END_TIME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(WipeOutContactHistoryPiiAction.PARAM_CUTOFF_TIME)
|
||||
static Optional<DateTime> provideCutoffTime(HttpServletRequest req) {
|
||||
return extractOptionalDatetimeParameter(req, WipeOutContactHistoryPiiAction.PARAM_CUTOFF_TIME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(ExpandRecurringBillingEventsAction.PARAM_ADVANCE_CURSOR)
|
||||
static boolean provideAdvanceCursor(HttpServletRequest req) {
|
||||
|
||||
@@ -37,7 +37,7 @@ import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
|
||||
@@ -14,31 +14,39 @@
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
import static google.registry.batch.BatchModule.PARAM_DRY_RUN;
|
||||
import static google.registry.beam.BeamUtils.createJobName;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateParameter;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateRequest;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateResponse;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.beam.wipeout.WipeOutContactHistoryPiiPipeline;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.request.Action;
|
||||
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.Clock;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Stream;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* An action that wipes out Personal Identifiable Information (PII) fields of {@link ContactHistory}
|
||||
* entities.
|
||||
* An action that launches {@link WipeOutContactHistoryPiiPipeline} to wipe out Personal
|
||||
* Identifiable Information (PII) fields of {@link ContactHistory} entities.
|
||||
*
|
||||
* <p>ContactHistory entities should be retained in the database for only certain amount of time.
|
||||
* This periodic wipe out action only applies to SQL.
|
||||
* <p>{@link ContactHistory} entities should be retained in the database for only certain amount of
|
||||
* time.
|
||||
*/
|
||||
@Action(
|
||||
service = Service.BACKEND,
|
||||
@@ -47,90 +55,89 @@ import org.joda.time.DateTime;
|
||||
public class WipeOutContactHistoryPiiAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/wipeOutContactHistoryPii";
|
||||
public static final String PARAM_CUTOFF_TIME = "wipeoutTime";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final String PIPELINE_NAME = "wipe_out_contact_history_pii_pipeline";
|
||||
|
||||
private final Clock clock;
|
||||
private final Response response;
|
||||
private final boolean isDryRun;
|
||||
private final Optional<DateTime> maybeCutoffTime;
|
||||
private final int minMonthsBeforeWipeOut;
|
||||
private final int wipeOutQueryBatchSize;
|
||||
private final String stagingBucketUrl;
|
||||
private final String projectId;
|
||||
private final String jobRegion;
|
||||
private final Dataflow dataflow;
|
||||
private final Response response;
|
||||
|
||||
@Inject
|
||||
public WipeOutContactHistoryPiiAction(
|
||||
Clock clock,
|
||||
@Parameter(PARAM_DRY_RUN) boolean isDryRun,
|
||||
@Parameter(PARAM_CUTOFF_TIME) Optional<DateTime> maybeCutoffTime,
|
||||
@Config("minMonthsBeforeWipeOut") int minMonthsBeforeWipeOut,
|
||||
@Config("wipeOutQueryBatchSize") int wipeOutQueryBatchSize,
|
||||
@Config("beamStagingBucketUrl") String stagingBucketUrl,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("defaultJobRegion") String jobRegion,
|
||||
Dataflow dataflow,
|
||||
Response response) {
|
||||
this.clock = clock;
|
||||
this.response = response;
|
||||
this.isDryRun = isDryRun;
|
||||
this.maybeCutoffTime = maybeCutoffTime;
|
||||
this.minMonthsBeforeWipeOut = minMonthsBeforeWipeOut;
|
||||
this.wipeOutQueryBatchSize = wipeOutQueryBatchSize;
|
||||
this.stagingBucketUrl = stagingBucketUrl;
|
||||
this.projectId = projectId;
|
||||
this.jobRegion = jobRegion;
|
||||
this.dataflow = dataflow;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
DateTime cutoffTime =
|
||||
maybeCutoffTime.orElse(clock.nowUtc().minusMonths(minMonthsBeforeWipeOut));
|
||||
LaunchFlexTemplateParameter launchParameter =
|
||||
new LaunchFlexTemplateParameter()
|
||||
.setJobName(
|
||||
createJobName(
|
||||
String.format(
|
||||
"contact-history-pii-wipeout-%s",
|
||||
cutoffTime.toString("yyyy-MM-dd't'HH-mm-ss'z'")),
|
||||
clock))
|
||||
.setContainerSpecGcsPath(
|
||||
String.format("%s/%s_metadata.json", stagingBucketUrl, PIPELINE_NAME))
|
||||
.setParameters(
|
||||
ImmutableMap.of(
|
||||
"registryEnvironment",
|
||||
RegistryEnvironment.get().name(),
|
||||
"cutoffTime",
|
||||
cutoffTime.toString("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
|
||||
"isDryRun",
|
||||
Boolean.toString(isDryRun)));
|
||||
logger.atInfo().log(
|
||||
"Launching Beam pipeline to wipe out all PII of contact history entities prior to %s%s.",
|
||||
cutoffTime, " in dry run mode");
|
||||
try {
|
||||
int totalNumOfWipedEntities = 0;
|
||||
DateTime wipeOutTime = clock.nowUtc().minusMonths(minMonthsBeforeWipeOut);
|
||||
logger.atInfo().log(
|
||||
"About to wipe out all PII of contact history entities prior to %s.", wipeOutTime);
|
||||
|
||||
int numOfWipedEntities = 0;
|
||||
do {
|
||||
numOfWipedEntities =
|
||||
tm().transact(
|
||||
() ->
|
||||
wipeOutContactHistoryData(
|
||||
getNextContactHistoryEntitiesWithPiiBatch(wipeOutTime)));
|
||||
totalNumOfWipedEntities += numOfWipedEntities;
|
||||
} while (numOfWipedEntities > 0);
|
||||
String msg =
|
||||
String.format(
|
||||
"Done. Wiped out PII of %d ContactHistory entities in total.",
|
||||
totalNumOfWipedEntities);
|
||||
logger.atInfo().log(msg);
|
||||
response.setPayload(msg);
|
||||
LaunchFlexTemplateResponse launchResponse =
|
||||
dataflow
|
||||
.projects()
|
||||
.locations()
|
||||
.flexTemplates()
|
||||
.launch(
|
||||
projectId,
|
||||
jobRegion,
|
||||
new LaunchFlexTemplateRequest().setLaunchParameter(launchParameter))
|
||||
.execute();
|
||||
logger.atInfo().log("Got response: %s", launchResponse.getJob().toPrettyString());
|
||||
response.setStatus(SC_OK);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Exception thrown during the process of wiping out contact history PII.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(
|
||||
String.format(
|
||||
"Exception thrown during the process of wiping out contact history PII with cause"
|
||||
+ ": %s",
|
||||
e));
|
||||
"Launched contact history PII wipeout pipeline: %s",
|
||||
launchResponse.getJob().getId()));
|
||||
} catch (IOException e) {
|
||||
logger.atWarning().withCause(e).log("Pipeline Launch failed");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Pipeline launch failed: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream of up to {@link #wipeOutQueryBatchSize} {@link ContactHistory} entities
|
||||
* containing PII that are prior to @param wipeOutTime.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
Stream<ContactHistory> getNextContactHistoryEntitiesWithPiiBatch(DateTime wipeOutTime) {
|
||||
// email is one of the required fields in EPP, meaning it's initially not null.
|
||||
// Therefore, checking if it's null is one way to avoid processing contact history entities
|
||||
// that have been processed previously. Refer to RFC 5733 for more information.
|
||||
return tm().query(
|
||||
"FROM ContactHistory WHERE modificationTime < :wipeOutTime " + "AND email IS NOT NULL",
|
||||
ContactHistory.class)
|
||||
.setParameter("wipeOutTime", wipeOutTime)
|
||||
.setMaxResults(wipeOutQueryBatchSize)
|
||||
.getResultStream();
|
||||
}
|
||||
|
||||
/** Wipes out the PII of each of the {@link ContactHistory} entities in the stream. */
|
||||
@VisibleForTesting
|
||||
int wipeOutContactHistoryData(Stream<ContactHistory> contactHistoryEntities) {
|
||||
AtomicInteger numOfEntities = new AtomicInteger(0);
|
||||
contactHistoryEntities.forEach(
|
||||
contactHistoryEntity -> {
|
||||
tm().update(contactHistoryEntity.asBuilder().wipeOutPii().build());
|
||||
numOfEntities.incrementAndGet();
|
||||
});
|
||||
logger.atInfo().log(
|
||||
"Wiped out all PII fields of %d ContactHistory entities.", numOfEntities.get());
|
||||
return numOfEntities.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.SystemClock;
|
||||
@@ -281,7 +281,7 @@ public class ExpandRecurringBillingEventsPipeline implements Serializable {
|
||||
return;
|
||||
}
|
||||
Domain domain = tm().loadByKey(Domain.createVKey(recurring.getDomainRepoId()));
|
||||
Registry tld = Registry.get(domain.getTld());
|
||||
Tld tld = Tld.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
|
||||
|
||||
@@ -33,7 +33,7 @@ import google.registry.model.common.Cursor;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.rde.RdeNamingUtils;
|
||||
import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.rde.BrdaCopyAction;
|
||||
import google.registry.rde.DepositFragment;
|
||||
import google.registry.rde.Ghostryde;
|
||||
@@ -272,12 +272,12 @@ public class RdeIO {
|
||||
tm().transact(
|
||||
() -> {
|
||||
PendingDeposit key = input.getKey();
|
||||
Registry registry = Registry.get(key.tld());
|
||||
Tld tld = Tld.get(key.tld());
|
||||
Optional<Cursor> cursor =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(
|
||||
Cursor.createScopedVKey(key.cursor(), registry)));
|
||||
Cursor.createScopedVKey(key.cursor(), tld)));
|
||||
DateTime position = getCursorTimeOrStartOfTime(cursor);
|
||||
checkState(key.interval() != null, "Interval must be present");
|
||||
DateTime newPosition = key.watermark().plus(key.interval());
|
||||
@@ -290,7 +290,7 @@ public class RdeIO {
|
||||
"Partial ordering of RDE deposits broken: %s %s",
|
||||
position,
|
||||
key);
|
||||
tm().put(Cursor.createScoped(key.cursor(), newPosition, registry));
|
||||
tm().put(Cursor.createScoped(key.cursor(), newPosition, tld));
|
||||
logger.atInfo().log(
|
||||
"Rolled forward %s on %s cursor to %s.", key.cursor(), key.tld(), newPosition);
|
||||
RdeRevision.saveRevision(key.tld(), key.watermark(), key.mode(), input.getValue());
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
// 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.beam.wipeout;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.voids;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.io.Serializable;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.coders.KvCoder;
|
||||
import org.apache.beam.sdk.coders.StringUtf8Coder;
|
||||
import org.apache.beam.sdk.coders.VarLongCoder;
|
||||
import org.apache.beam.sdk.metrics.Counter;
|
||||
import org.apache.beam.sdk.metrics.Metrics;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.apache.beam.sdk.transforms.MapElements;
|
||||
import org.apache.beam.sdk.transforms.join.CoGroupByKey;
|
||||
import org.apache.beam.sdk.transforms.join.KeyedPCollectionTuple;
|
||||
import org.apache.beam.sdk.values.KV;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
import org.apache.beam.sdk.values.TupleTag;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Definition of a Dataflow Flex pipeline template, which finds out {@link ContactHistory} entries
|
||||
* that are older than a given age (excluding the most recent one, even if it falls with the range)
|
||||
* and wipe out PII information in them.
|
||||
*
|
||||
* <p>To stage this template locally, run {@code ./nom_build :core:sBP --environment=alpha \
|
||||
* --pipeline=wipeOutContactHistoryPii}.
|
||||
*
|
||||
* <p>Then, you can run the staged template via the API client library, gCloud or a raw REST call.
|
||||
*/
|
||||
public class WipeOutContactHistoryPiiPipeline implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -4111052675715913820L;
|
||||
private static final TupleTag<Long> REVISIONS_TO_WIPE = new TupleTag<>();
|
||||
private static final TupleTag<Long> MOST_RECENT_REVISION = new TupleTag<>();
|
||||
|
||||
private final DateTime cutoffTime;
|
||||
private final boolean dryRun;
|
||||
private final Counter contactsInScope =
|
||||
Metrics.counter("WipeOutContactHistoryPii", "contacts in scope");
|
||||
private final Counter historiesToWipe =
|
||||
Metrics.counter("WipeOutContactHistoryPii", "contact histories to wipe PII from");
|
||||
private final Counter historiesWiped =
|
||||
Metrics.counter("WipeOutContactHistoryPii", "contact histories actually updated");
|
||||
|
||||
WipeOutContactHistoryPiiPipeline(WipeOutContactHistoryPiiPipelineOptions options) {
|
||||
dryRun = options.getIsDryRun();
|
||||
cutoffTime = DateTime.parse(options.getCutoffTime());
|
||||
}
|
||||
|
||||
void setup(Pipeline pipeline) {
|
||||
KeyedPCollectionTuple.of(REVISIONS_TO_WIPE, getHistoryEntriesToWipe(pipeline))
|
||||
.and(MOST_RECENT_REVISION, getMostRecentHistoryEntries(pipeline))
|
||||
.apply("Group by contact", CoGroupByKey.create())
|
||||
.apply(
|
||||
"Wipe out PII",
|
||||
MapElements.into(voids())
|
||||
.via(
|
||||
kv -> {
|
||||
String repoId = kv.getKey();
|
||||
long mostRecentRevision = kv.getValue().getOnly(MOST_RECENT_REVISION);
|
||||
ImmutableList<Long> revisionsToWipe =
|
||||
Streams.stream(kv.getValue().getAll(REVISIONS_TO_WIPE))
|
||||
.filter(e -> e != mostRecentRevision)
|
||||
.collect(toImmutableList());
|
||||
if (revisionsToWipe.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
contactsInScope.inc();
|
||||
tm().transact(
|
||||
() -> {
|
||||
for (long revisionId : revisionsToWipe) {
|
||||
historiesToWipe.inc();
|
||||
ContactHistory history =
|
||||
tm().loadByKey(
|
||||
VKey.create(
|
||||
ContactHistory.class,
|
||||
new HistoryEntryId(repoId, revisionId)));
|
||||
// In the unlikely case where multiple pipelines run at the
|
||||
// same time, or where the runner decides to rerun a particular
|
||||
// transform, we might have a history entry that has already been
|
||||
// wiped at this point. There's no need to wipe it again.
|
||||
if (!dryRun
|
||||
&& history.getContactBase().isPresent()
|
||||
&& history.getContactBase().get().getEmailAddress() != null) {
|
||||
historiesWiped.inc();
|
||||
tm().update(history.asBuilder().wipeOutPii().build());
|
||||
}
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
PCollection<KV<String, Long>> getHistoryEntriesToWipe(Pipeline pipeline) {
|
||||
return pipeline.apply(
|
||||
"Find contact histories to wipee",
|
||||
// Email is one of the required fields in EPP, meaning it's initially not null when it
|
||||
// is set by EPP flows (even though it is nullalbe in the SQL schema). Therefore,
|
||||
// checking if it's null is one way to avoid processing contact history entities that
|
||||
// have been processed previously. Refer to RFC 5733 for more information.
|
||||
RegistryJpaIO.read(
|
||||
"SELECT repoId, revisionId FROM ContactHistory WHERE email IS NOT NULL AND"
|
||||
+ " modificationTime < :cutoffTime",
|
||||
ImmutableMap.of("cutoffTime", cutoffTime),
|
||||
Object[].class,
|
||||
row -> KV.of((String) row[0], (long) row[1]))
|
||||
.withCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of())));
|
||||
}
|
||||
|
||||
PCollection<KV<String, Long>> getMostRecentHistoryEntries(Pipeline pipeline) {
|
||||
return pipeline.apply(
|
||||
"Find the most recent historiy entry for each contact",
|
||||
RegistryJpaIO.read(
|
||||
"SELECT repoId, revisionId FROM ContactHistory"
|
||||
+ " WHERE (repoId, modificationTime) IN"
|
||||
+ " (SELECT repoId, MAX(modificationTime) FROM ContactHistory GROUP BY repoId)",
|
||||
ImmutableMap.of(),
|
||||
Object[].class,
|
||||
row -> KV.of((String) row[0], (long) row[1]))
|
||||
.withCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of())));
|
||||
}
|
||||
|
||||
PipelineResult run(Pipeline pipeline) {
|
||||
setup(pipeline);
|
||||
return pipeline.run();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
PipelineOptionsFactory.register(WipeOutContactHistoryPiiPipelineOptions.class);
|
||||
WipeOutContactHistoryPiiPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(args)
|
||||
.withValidation()
|
||||
.as(WipeOutContactHistoryPiiPipelineOptions.class);
|
||||
// Repeatable read should be more than enough since we are dealing with old history entries that
|
||||
// are otherwise immutable.
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ);
|
||||
Pipeline pipeline = Pipeline.create(options);
|
||||
new WipeOutContactHistoryPiiPipeline(options).run(pipeline);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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.beam.wipeout;
|
||||
|
||||
import google.registry.beam.common.RegistryPipelineOptions;
|
||||
import org.apache.beam.sdk.options.Default;
|
||||
import org.apache.beam.sdk.options.Description;
|
||||
|
||||
public interface WipeOutContactHistoryPiiPipelineOptions extends RegistryPipelineOptions {
|
||||
|
||||
@Description(
|
||||
"A contact history entry with a history modification time before this time will have its PII"
|
||||
+ " wiped, unless it is the most entry for the contact.")
|
||||
String getCutoffTime();
|
||||
|
||||
void setCutoffTime(String value);
|
||||
|
||||
@Description(
|
||||
"If true, the wiped out billing events will not be saved but the pipeline metrics counter"
|
||||
+ " will still be updated.")
|
||||
@Default.Boolean(false)
|
||||
boolean getIsDryRun();
|
||||
|
||||
void setIsDryRun(boolean value);
|
||||
}
|
||||
@@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.dns.ReadDnsRefreshRequestsAction;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.util.YamlUtils;
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -61,7 +63,7 @@ import org.joda.time.Duration;
|
||||
* <p>This class does not represent the total configuration of the Nomulus service. It's <b>only
|
||||
* meant for settings that need to be configured <i>once</i></b>. Settings which may be subject to
|
||||
* change in the future, should instead be retrieved from the database. The {@link
|
||||
* google.registry.model.tld.Registry Registry} class is one such example of this.
|
||||
* google.registry.model.tld.Tld Tld} class is one such example of this.
|
||||
*
|
||||
* <p>Note: Only settings that are actually configurable belong in this file. It's not a catch-all
|
||||
* for anything widely used throughout the code base.
|
||||
@@ -294,9 +296,9 @@ public final class RegistryConfig {
|
||||
|
||||
/**
|
||||
* The maximum number of domain and host updates to batch together to send to
|
||||
* PublishDnsUpdatesAction, to avoid exceeding AppEngine's limits.
|
||||
* PublishDnsUpdatesAction, to avoid exceeding HTTP request timeout limits.
|
||||
*
|
||||
* @see google.registry.dns.ReadDnsQueueAction
|
||||
* @see google.registry.dns.ReadDnsRefreshRequestsAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("dnsTldUpdateBatchSize")
|
||||
@@ -327,23 +329,30 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* The requested maximum duration for ReadDnsQueueAction.
|
||||
* The requested maximum duration for {@link ReadDnsRefreshRequestsAction}.
|
||||
*
|
||||
* <p>ReadDnsQueueAction reads update tasks from the dns-pull queue. It will continue reading
|
||||
* tasks until either the queue is empty, or this duration has passed.
|
||||
* <p>{@link ReadDnsRefreshRequestsAction} reads refresh requests from {@link DnsRefreshRequest}
|
||||
* It will continue reading requests until either no requests exist that matche the condition,
|
||||
* or this duration has passed.
|
||||
*
|
||||
* <p>This time is the maximum duration between the first and last attempt to lease tasks from
|
||||
* the dns-pull queue. The actual running time might be slightly longer, as we process the
|
||||
* tasks.
|
||||
* <p>This time is the maximum duration between the first and last attempt to read requests from
|
||||
* {@link DnsRefreshRequest}. The actual running time might be slightly longer, as we process
|
||||
* the requests.
|
||||
*
|
||||
* <p>This value should be less than the cron-job repeat rate for ReadDnsQueueAction, to make
|
||||
* sure we don't have multiple ReadDnsActions leasing tasks simultaneously.
|
||||
* <p>The requests that are read will not be read again by any action until after this period
|
||||
* has passed, so concurrent runs (or runs that are very close to each other) of {@link
|
||||
* ReadDnsRefreshRequestsAction} will not keep reading the same requests with the earliest
|
||||
* request time.
|
||||
*
|
||||
* @see google.registry.dns.ReadDnsQueueAction
|
||||
* <p>Still, this value should ideally be less than the cloud scheduler job repeat rate for
|
||||
* {@link ReadDnsRefreshRequestsAction}, to not waste resources on multiple actions running at
|
||||
* the same time.
|
||||
*
|
||||
* <p>see google.registry.dns.ReadDnsRefreshRequestsAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("readDnsQueueActionRuntime")
|
||||
public static Duration provideReadDnsQueueRuntime() {
|
||||
@Config("readDnsRefreshRequestsActionRuntime")
|
||||
public static Duration provideReadDnsRefreshRequestsRuntime() {
|
||||
return Duration.standardSeconds(45);
|
||||
}
|
||||
|
||||
@@ -1316,12 +1325,6 @@ public final class RegistryConfig {
|
||||
return config.contactHistory.minMonthsBeforeWipeOut;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("wipeOutQueryBatchSize")
|
||||
public static int provideWipeOutQueryBatchSize(RegistryConfigSettings config) {
|
||||
return config.contactHistory.wipeOutQueryBatchSize;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("jdbcBatchSize")
|
||||
public static int provideHibernateJdbcBatchSize(RegistryConfigSettings config) {
|
||||
|
||||
@@ -243,7 +243,6 @@ public class RegistryConfigSettings {
|
||||
/** Configuration for contact history. */
|
||||
public static class ContactHistory {
|
||||
public int minMonthsBeforeWipeOut;
|
||||
public int wipeOutQueryBatchSize;
|
||||
}
|
||||
|
||||
/** Configuration for dns update. */
|
||||
|
||||
@@ -474,8 +474,6 @@ registryTool:
|
||||
contactHistory:
|
||||
# The number of months that a ContactHistory entity should be stored in the database.
|
||||
minMonthsBeforeWipeOut: 18
|
||||
# The batch size for querying ContactHistory table in the database.
|
||||
wipeOutQueryBatchSize: 500
|
||||
|
||||
# Configuration options relevant to the DNS update functionality.
|
||||
dnsUpdate:
|
||||
|
||||
@@ -28,8 +28,8 @@ import static google.registry.cron.CronModule.JITTER_SECONDS_PARAM;
|
||||
import static google.registry.cron.CronModule.QUEUE_PARAM;
|
||||
import static google.registry.cron.CronModule.RUN_IN_EMPTY_PARAM;
|
||||
import static google.registry.model.tld.Registries.getTldsOfType;
|
||||
import static google.registry.model.tld.Registry.TldType.REAL;
|
||||
import static google.registry.model.tld.Registry.TldType.TEST;
|
||||
import static google.registry.model.tld.Tld.TldType.REAL;
|
||||
import static google.registry.model.tld.Tld.TldType.TEST;
|
||||
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
|
||||
@@ -19,6 +19,8 @@ import static google.registry.dns.DnsConstants.DNS_PULL_QUEUE_NAME;
|
||||
import static google.registry.dns.RefreshDnsOnHostRenameAction.PARAM_HOST_KEY;
|
||||
import static google.registry.request.RequestParameters.extractEnumParameter;
|
||||
import static google.registry.request.RequestParameters.extractIntParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
import static google.registry.request.RequestParameters.extractSetOfParameters;
|
||||
|
||||
@@ -33,6 +35,7 @@ import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.writer.DnsWriterZone;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Named;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -48,7 +51,10 @@ public abstract class DnsModule {
|
||||
public static final String PARAM_DOMAINS = "domains";
|
||||
public static final String PARAM_HOSTS = "hosts";
|
||||
public static final String PARAM_PUBLISH_TASK_ENQUEUED = "enqueued";
|
||||
public static final String PARAM_REFRESH_REQUEST_CREATED = "itemsCreated";
|
||||
public static final String PARAM_REFRESH_REQUEST_TIME = "requestTime";
|
||||
// This parameter cannot be named "jitterSeconds", which will be read by TldFanoutAction and not
|
||||
// be passed down to actions.
|
||||
public static final String PARAM_DNS_JITTER_SECONDS = "dnsJitterSeconds";
|
||||
|
||||
@Binds
|
||||
@DnsWriterZone
|
||||
@@ -83,10 +89,13 @@ public abstract class DnsModule {
|
||||
return DateTime.parse(extractRequiredParameter(req, PARAM_PUBLISH_TASK_ENQUEUED));
|
||||
}
|
||||
|
||||
// TODO: Retire the old header after DNS pull queue migration.
|
||||
@Provides
|
||||
@Parameter(PARAM_REFRESH_REQUEST_CREATED)
|
||||
@Parameter(PARAM_REFRESH_REQUEST_TIME)
|
||||
static DateTime provideItemsCreateTime(HttpServletRequest req) {
|
||||
return DateTime.parse(extractRequiredParameter(req, PARAM_REFRESH_REQUEST_CREATED));
|
||||
return DateTime.parse(
|
||||
extractOptionalParameter(req, "itemsCreated")
|
||||
.orElse(extractRequiredParameter(req, PARAM_REFRESH_REQUEST_TIME)));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -125,6 +134,12 @@ public abstract class DnsModule {
|
||||
return extractRequiredParameter(req, PARAM_HOST_KEY);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_DNS_JITTER_SECONDS)
|
||||
static Optional<Integer> provideJitterSeconds(HttpServletRequest req) {
|
||||
return extractOptionalIntParameter(req, PARAM_DNS_JITTER_SECONDS);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("domainOrHostName")
|
||||
static String provideName(HttpServletRequest req) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.util.Clock;
|
||||
@@ -54,13 +55,13 @@ import org.joda.time.Duration;
|
||||
* <p>This includes a {@link RateLimiter} to limit the {@link Queue#leaseTasks} call rate to 9 QPS,
|
||||
* to stay under the 10 QPS limit for this function.
|
||||
*
|
||||
* <p>Note that overlapping calls to {@link ReadDnsQueueAction} (the only place where
|
||||
* {@link DnsQueue#leaseTasks} is used) will have different rate limiters, so they could exceed the
|
||||
* allowed rate. This should be rare though - because {@link DnsQueue#leaseTasks} is only used in
|
||||
* {@link ReadDnsQueueAction}, which is run as a cron job with running time shorter than the cron
|
||||
* repeat time - meaning there should never be two instances running at once.
|
||||
* <p>Note that overlapping calls to {@link ReadDnsQueueAction} (the only place where {@link
|
||||
* DnsQueue#leaseTasks} is used) will have different rate limiters, so they could exceed the allowed
|
||||
* rate. This should be rare though - because {@link DnsQueue#leaseTasks} is only used in {@link
|
||||
* ReadDnsQueueAction}, which is run as a cron job with running time shorter than the cron repeat
|
||||
* time - meaning there should never be two instances running at once.
|
||||
*
|
||||
* @see google.registry.config.RegistryConfig.ConfigModule#provideReadDnsQueueRuntime
|
||||
* @see RegistryConfig.ConfigModule#provideReadDnsRefreshRequestsRuntime()
|
||||
*/
|
||||
public class DnsQueue {
|
||||
|
||||
|
||||
@@ -14,15 +14,19 @@
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
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 java.util.Collection;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Utility class to handle DNS refresh requests. */
|
||||
@@ -67,6 +71,61 @@ public class DnsUtils {
|
||||
requestDnsRefresh(hostName, TargetType.HOST, Duration.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns pending DNS update requests that need further processing up to batch size, in ascending
|
||||
* order of their request time, and updates their processing time to now.
|
||||
*
|
||||
* <p>The criteria to pick the requests to include are:
|
||||
*
|
||||
* <ul>
|
||||
* <li>They are for the given TLD.
|
||||
* <li>Their request time is not in the future.
|
||||
* <li>The last time they were processed is before the cooldown period.
|
||||
* </ul>
|
||||
*/
|
||||
public ImmutableList<DnsRefreshRequest> readAndUpdateRequestsWithLatestProcessTime(
|
||||
String tld, Duration cooldown, int batchSize) {
|
||||
return tm().transact(
|
||||
() -> {
|
||||
DateTime transactionTime = tm().getTransactionTime();
|
||||
ImmutableList<DnsRefreshRequest> requests =
|
||||
tm().query(
|
||||
"FROM DnsRefreshRequest WHERE tld = :tld "
|
||||
+ "AND requestTime <= :now AND lastProcessTime < :cutoffTime "
|
||||
+ "ORDER BY requestTime ASC, id ASC",
|
||||
DnsRefreshRequest.class)
|
||||
.setParameter("tld", tld)
|
||||
.setParameter("now", transactionTime)
|
||||
.setParameter("cutoffTime", transactionTime.minus(cooldown))
|
||||
.setMaxResults(batchSize)
|
||||
.getResultStream()
|
||||
// Note that the process time is when the request was last read, batched and
|
||||
// queued up for publishing, not when it is actually published by the DNS
|
||||
// writer. This timestamp acts as a cooldown so the same request will not be
|
||||
// retried too frequently. See DnsRefreshRequest.getLastProcessTime for a
|
||||
// detailed explaination.
|
||||
.map(e -> e.updateProcessTime(transactionTime))
|
||||
.collect(toImmutableList());
|
||||
tm().updateAll(requests);
|
||||
return requests;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the requests that have been processed.
|
||||
*
|
||||
* <p>Note that if a request entity has already been deleted, the method still succeeds without
|
||||
* error because all we care about is that it no longer exists after the method runs.
|
||||
*/
|
||||
public void deleteRequests(Collection<DnsRefreshRequest> requests) {
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().delete(
|
||||
requests.stream()
|
||||
.map(DnsRefreshRequest::createVKey)
|
||||
.collect(toImmutableList())));
|
||||
}
|
||||
|
||||
private boolean usePullQueue() {
|
||||
return !DatabaseMigrationStateSchedule.getValueAtTime(dnsQueue.getClock().nowUtc())
|
||||
.equals(MigrationState.DNS_SQL);
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.dns.writer.DnsWriter;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class DnsWriterProxy {
|
||||
* If the DnsWriter doesn't belong to this TLD, will return null.
|
||||
*/
|
||||
public DnsWriter getByClassNameForTld(String className, String tld) {
|
||||
if (!Registry.get(tld).getDnsWriters().contains(className)) {
|
||||
if (!Tld.get(tld).getDnsWriters().contains(className)) {
|
||||
logger.atWarning().log(
|
||||
"Loaded potentially stale DNS writer %s which is not active on TLD %s.", className, tld);
|
||||
return null;
|
||||
|
||||
@@ -22,7 +22,7 @@ import static google.registry.dns.DnsModule.PARAM_HOSTS;
|
||||
import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_CREATED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_TIME;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
@@ -45,7 +45,7 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Header;
|
||||
@@ -96,8 +96,8 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
*
|
||||
* <p>This comes from the fanout in {@link ReadDnsQueueAction} which dispatches each batch to be
|
||||
* published by each DNS writer on the TLD. So this field contains the value of one of the DNS
|
||||
* writers configured in {@link Registry#getDnsWriters()}, as of the time the batch was written
|
||||
* out (and not necessarily currently).
|
||||
* writers configured in {@link Tld#getDnsWriters()}, as of the time the batch was written out
|
||||
* (and not necessarily currently).
|
||||
*/
|
||||
private final String dnsWriter;
|
||||
|
||||
@@ -124,7 +124,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
public PublishDnsUpdatesAction(
|
||||
@Parameter(PARAM_DNS_WRITER) String dnsWriter,
|
||||
@Parameter(PARAM_PUBLISH_TASK_ENQUEUED) DateTime enqueuedTime,
|
||||
@Parameter(PARAM_REFRESH_REQUEST_CREATED) DateTime itemsCreateTime,
|
||||
@Parameter(PARAM_REFRESH_REQUEST_TIME) DateTime itemsCreateTime,
|
||||
@Parameter(PARAM_LOCK_INDEX) int lockIndex,
|
||||
@Parameter(PARAM_NUM_PUBLISH_LOCKS) int numPublishLocks,
|
||||
@Parameter(PARAM_DOMAINS) Set<String> domains,
|
||||
@@ -346,7 +346,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
.put(PARAM_LOCK_INDEX, Integer.toString(lockIndex))
|
||||
.put(PARAM_NUM_PUBLISH_LOCKS, Integer.toString(numPublishLocks))
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.nowUtc().toString())
|
||||
.put(PARAM_REFRESH_REQUEST_CREATED, itemsCreateTime.toString())
|
||||
.put(PARAM_REFRESH_REQUEST_TIME, itemsCreateTime.toString())
|
||||
.put(PARAM_DOMAINS, Joiner.on(",").join(domains))
|
||||
.put(PARAM_HOSTS, Joiner.on(",").join(hosts))
|
||||
.build()));
|
||||
@@ -372,11 +372,11 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
return false;
|
||||
}
|
||||
// Check if the Registry object's num locks has changed since this task was batched
|
||||
int registryNumPublishLocks = Registry.get(tld).getNumDnsPublishLocks();
|
||||
if (registryNumPublishLocks != numPublishLocks) {
|
||||
int tldNumPublishLocks = Tld.get(tld).getNumDnsPublishLocks();
|
||||
if (tldNumPublishLocks != numPublishLocks) {
|
||||
logger.atWarning().log(
|
||||
"Registry numDnsPublishLocks %d out of sync with parameter %d.",
|
||||
registryNumPublishLocks, numPublishLocks);
|
||||
tldNumPublishLocks, numPublishLocks);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -26,7 +26,7 @@ import static google.registry.dns.DnsModule.PARAM_HOSTS;
|
||||
import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_CREATED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_TIME;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.util.DomainNameUtils.getSecondLevelDomain;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -48,7 +48,7 @@ import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
@@ -109,7 +109,7 @@ public final class ReadDnsQueueAction implements Runnable {
|
||||
@Inject
|
||||
ReadDnsQueueAction(
|
||||
@Config("dnsTldUpdateBatchSize") int tldUpdateBatchSize,
|
||||
@Config("readDnsQueueActionRuntime") Duration requestedMaximumDuration,
|
||||
@Config("readDnsRefreshRequestsActionRuntime") Duration requestedMaximumDuration,
|
||||
@Parameter(PARAM_JITTER_SECONDS) Optional<Integer> jitterSeconds,
|
||||
Clock clock,
|
||||
DnsQueue dnsQueue,
|
||||
@@ -292,7 +292,7 @@ public final class ReadDnsQueueAction implements Runnable {
|
||||
} else if (!tlds.contains(tld)) {
|
||||
classifiedTasksBuilder.tasksToKeepBuilder().add(task);
|
||||
classifiedTasksBuilder.unknownTldsBuilder().add(tld);
|
||||
} else if (Registry.get(tld).getDnsPaused()) {
|
||||
} else if (Tld.get(tld).getDnsPaused()) {
|
||||
classifiedTasksBuilder.tasksToKeepBuilder().add(task);
|
||||
classifiedTasksBuilder.pausedTldsBuilder().add(tld);
|
||||
} else {
|
||||
@@ -330,7 +330,7 @@ public final class ReadDnsQueueAction implements Runnable {
|
||||
for (Map.Entry<String, Collection<RefreshItem>> tldRefreshItemsEntry
|
||||
: refreshItemsByTld.asMap().entrySet()) {
|
||||
String tld = tldRefreshItemsEntry.getKey();
|
||||
int numPublishLocks = Registry.get(tld).getNumDnsPublishLocks();
|
||||
int numPublishLocks = Tld.get(tld).getNumDnsPublishLocks();
|
||||
// 1 lock or less implies no TLD-wide locks, simply enqueue everything under lock 1 of 1
|
||||
if (numPublishLocks <= 1) {
|
||||
enqueueUpdates(tld, 1, 1, tldRefreshItemsEntry.getValue());
|
||||
@@ -368,7 +368,7 @@ public final class ReadDnsQueueAction implements Runnable {
|
||||
for (List<RefreshItem> chunk : Iterables.partition(items, tldUpdateBatchSize)) {
|
||||
DateTime earliestCreateTime =
|
||||
chunk.stream().map(RefreshItem::creationTime).min(Comparator.naturalOrder()).get();
|
||||
for (String dnsWriter : Registry.get(tld).getDnsWriters()) {
|
||||
for (String dnsWriter : Tld.get(tld).getDnsWriters()) {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
PublishDnsUpdatesAction.PATH,
|
||||
@@ -379,7 +379,7 @@ public final class ReadDnsQueueAction implements Runnable {
|
||||
.put(PARAM_LOCK_INDEX, Integer.toString(lockIndex))
|
||||
.put(PARAM_NUM_PUBLISH_LOCKS, Integer.toString(numPublishLocks))
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.nowUtc().toString())
|
||||
.put(PARAM_REFRESH_REQUEST_CREATED, earliestCreateTime.toString())
|
||||
.put(PARAM_REFRESH_REQUEST_TIME, earliestCreateTime.toString())
|
||||
.put(
|
||||
PARAM_DOMAINS,
|
||||
chunk.stream()
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
// 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 com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_JITTER_SECONDS;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_WRITER;
|
||||
import static google.registry.dns.DnsModule.PARAM_DOMAINS;
|
||||
import static google.registry.dns.DnsModule.PARAM_HOSTS;
|
||||
import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_TIME;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DomainNameUtils.getSecondLevelDomain;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
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.common.DnsRefreshRequest;
|
||||
import google.registry.model.tld.Tld;
|
||||
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.util.Clock;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Action for fanning out DNS refresh tasks by TLD, using data taken from {@link DnsRefreshRequest}
|
||||
* table.
|
||||
*/
|
||||
@Action(
|
||||
service = Service.BACKEND,
|
||||
path = "/_dr/task/readDnsRefreshRequests",
|
||||
automaticallyPrintOk = true,
|
||||
method = POST,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final int tldUpdateBatchSize;
|
||||
private final Duration requestedMaximumDuration;
|
||||
private final Optional<Integer> jitterSeconds;
|
||||
private final String tld;
|
||||
private final Clock clock;
|
||||
private final DnsUtils dnsUtils;
|
||||
private final HashFunction hashFunction;
|
||||
private final CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject
|
||||
ReadDnsRefreshRequestsAction(
|
||||
@Config("dnsTldUpdateBatchSize") int tldUpdateBatchSize,
|
||||
@Config("readDnsRefreshRequestsActionRuntime") Duration requestedMaximumDuration,
|
||||
@Parameter(PARAM_DNS_JITTER_SECONDS) Optional<Integer> jitterSeconds,
|
||||
@Parameter(PARAM_TLD) String tld,
|
||||
Clock clock,
|
||||
DnsUtils dnsUtils,
|
||||
HashFunction hashFunction,
|
||||
CloudTasksUtils cloudTasksUtils) {
|
||||
this.tldUpdateBatchSize = tldUpdateBatchSize;
|
||||
this.requestedMaximumDuration = requestedMaximumDuration;
|
||||
this.jitterSeconds = jitterSeconds;
|
||||
this.tld = tld;
|
||||
this.clock = clock;
|
||||
this.dnsUtils = dnsUtils;
|
||||
this.hashFunction = hashFunction;
|
||||
this.cloudTasksUtils = cloudTasksUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads requests up to the maximum requested runtime, and enqueues update batches from the these
|
||||
* requests.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
if (Tld.get(tld).getDnsPaused()) {
|
||||
logger.atInfo().log("The queue updated is paused for TLD: %s.", tld);
|
||||
return;
|
||||
}
|
||||
DateTime requestedEndTime = clock.nowUtc().plus(requestedMaximumDuration);
|
||||
// See getLockIndex(), requests are evenly distributed to [1, numDnsPublishLocks], so each
|
||||
// bucket would be roughly the size of tldUpdateBatchSize.
|
||||
int processBatchSize = tldUpdateBatchSize * Tld.get(tld).getNumDnsPublishLocks();
|
||||
while (requestedEndTime.isAfter(clock.nowUtc())) {
|
||||
ImmutableList<DnsRefreshRequest> requests =
|
||||
dnsUtils.readAndUpdateRequestsWithLatestProcessTime(
|
||||
tld, requestedMaximumDuration, processBatchSize);
|
||||
logger.atInfo().log("Read %d DNS update requests for TLD %s.", requests.size(), tld);
|
||||
if (!requests.isEmpty()) {
|
||||
processRequests(requests);
|
||||
}
|
||||
if (requests.size() < processBatchSize) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Subdivides {@link DnsRefreshRequest} into buckets by lock index, enqueue a Cloud Tasks task per
|
||||
* bucket, and then delete the requests in each bucket.
|
||||
*/
|
||||
void processRequests(Collection<DnsRefreshRequest> requests) {
|
||||
int numPublishLocks = Tld.get(tld).getNumDnsPublishLocks();
|
||||
requests.stream()
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
request -> getLockIndex(numPublishLocks, request), request -> request))
|
||||
.asMap()
|
||||
.forEach(
|
||||
(lockIndex, bucketedRequests) -> {
|
||||
try {
|
||||
enqueueUpdates(lockIndex, numPublishLocks, bucketedRequests);
|
||||
dnsUtils.deleteRequests(bucketedRequests);
|
||||
logger.atInfo().log(
|
||||
"Processed %d DNS update requests for TLD %s.", bucketedRequests.size(), tld);
|
||||
} catch (Exception e) {
|
||||
// Log but continue to process the next bucket. The failed tasks will NOT be
|
||||
// deleted and will be retried after the cooldown period has passed.
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Error processing DNS update requests: %s", bucketedRequests);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lock index for a given {@link DnsRefreshRequest}.
|
||||
*
|
||||
* <p>We hash the second level domain for all records, to group in-bailiwick hosts (the only ones
|
||||
* we refresh DNS for) with their superordinate domains. We use consistent hashing to determine
|
||||
* the lock index because it gives us [0,N) bucketing properties out of the box, then add 1 to
|
||||
* make indexes within [1,N].
|
||||
*/
|
||||
int getLockIndex(int numPublishLocks, DnsRefreshRequest request) {
|
||||
String domain = getSecondLevelDomain(request.getName(), tld);
|
||||
return Hashing.consistentHash(hashFunction.hashString(domain, UTF_8), numPublishLocks) + 1;
|
||||
}
|
||||
|
||||
/** Creates DNS refresh tasks for all writers for the tld within a lock index. */
|
||||
void enqueueUpdates(int lockIndex, int numPublishLocks, Collection<DnsRefreshRequest> requests) {
|
||||
ImmutableList.Builder<String> domainsBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<String> hostsBuilder = new ImmutableList.Builder<>();
|
||||
DateTime earliestRequestTime = END_OF_TIME;
|
||||
for (DnsRefreshRequest request : requests) {
|
||||
if (request.getRequestTime().isBefore(earliestRequestTime)) {
|
||||
earliestRequestTime = request.getRequestTime();
|
||||
}
|
||||
String name = request.getName();
|
||||
if (request.getType().equals(TargetType.DOMAIN)) {
|
||||
domainsBuilder.add(name);
|
||||
} else {
|
||||
hostsBuilder.add(name);
|
||||
}
|
||||
}
|
||||
ImmutableList<String> domains = domainsBuilder.build();
|
||||
ImmutableList<String> hosts = hostsBuilder.build();
|
||||
for (String dnsWriter : Tld.get(tld).getDnsWriters()) {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
PublishDnsUpdatesAction.PATH,
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.<String, String>builder()
|
||||
.put(PARAM_TLD, tld)
|
||||
.put(PARAM_DNS_WRITER, dnsWriter)
|
||||
.put(PARAM_LOCK_INDEX, Integer.toString(lockIndex))
|
||||
.put(PARAM_NUM_PUBLISH_LOCKS, Integer.toString(numPublishLocks))
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.nowUtc().toString())
|
||||
.put(PARAM_REFRESH_REQUEST_TIME, earliestRequestTime.toString())
|
||||
.put(PARAM_DOMAINS, Joiner.on(',').join(domains))
|
||||
.put(PARAM_HOSTS, Joiner.on(',').join(hosts))
|
||||
.build(),
|
||||
jitterSeconds);
|
||||
cloudTasksUtils.enqueue(DNS_PUBLISH_PUSH_QUEUE_NAME, task);
|
||||
logger.atInfo().log(
|
||||
"Enqueued DNS update request for (TLD %s, lock %d) with %d domains and %d hosts.",
|
||||
tld, lockIndex, domains.size(), hosts.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,4 +137,14 @@
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
<name>readDnsRefreshRequests</name>
|
||||
<description>
|
||||
Enqueue a ReadDnsRefreshRequestAction for each TLD.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
|
||||
@@ -157,6 +157,12 @@
|
||||
<url-pattern>/_dr/cron/readDnsQueue</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Reads the DNS refresh requests and kick off the appropriate tasks to update zone. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
<url-pattern>/_dr/task/readDnsRefreshRequests</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Publishes DNS updates. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
<mode>pull</mode>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for reading DNS update requests and batching them off to the dns-publish queue. -->
|
||||
<queue>
|
||||
<name>dns-refresh</name>
|
||||
<rate>100/s</rate>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for publishing DNS updates in batches. -->
|
||||
<queue>
|
||||
<name>dns-publish</name>
|
||||
|
||||
@@ -139,6 +139,16 @@
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
<name>readDnsRefreshRequests</name>
|
||||
<description>
|
||||
Enqueue a ReadDnsRefreshRequestAction for each TLD.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/deleteExpiredDomains]]></url>
|
||||
<name>deleteExpiredDomains</name>
|
||||
|
||||
@@ -210,6 +210,16 @@
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
<name>readDnsRefreshRequests</name>
|
||||
<description>
|
||||
Enqueue a ReadDnsRefreshRequestAction for each TLD.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=retryable-cron-tasks&endpoint=/_dr/task/icannReportingStaging&runInEmpty]]></url>
|
||||
<name>icannReportingStaging</name>
|
||||
|
||||
@@ -21,6 +21,16 @@
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
<name>readDnsRefreshRequests</name>
|
||||
<description>
|
||||
Enqueue a ReadDnsRefreshRequestAction for each TLD.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=sheet&endpoint=/_dr/task/syncRegistrarsSheet&runInEmpty]]></url>
|
||||
<name>syncRegistrarsSheet</name>
|
||||
|
||||
@@ -152,6 +152,16 @@
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
<name>readDnsRefreshRequests</name>
|
||||
<description>
|
||||
Enqueue a ReadDnsRefreshRequestAction for each TLD.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutContactHistoryPii]]></url>
|
||||
<name>wipeOutContactHistoryPii</name>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--TODO: ptkach - Remove this file after cloud scheduler is fully up and running -->
|
||||
<cronentries>
|
||||
|
||||
</cronentries>
|
||||
@@ -27,8 +27,8 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.storage.drive.DriveConnection;
|
||||
@@ -106,27 +106,28 @@ public class ExportDomainListsAction implements Runnable {
|
||||
}
|
||||
|
||||
protected static boolean exportToDrive(
|
||||
String tld, String domains, DriveConnection driveConnection) {
|
||||
String tldStr, String domains, DriveConnection driveConnection) {
|
||||
verifyNotNull(driveConnection, "Expecting non-null driveConnection");
|
||||
try {
|
||||
Registry registry = Registry.get(tld);
|
||||
if (registry.getDriveFolderId() == null) {
|
||||
Tld tld = Tld.get(tldStr);
|
||||
if (tld.getDriveFolderId() == null) {
|
||||
logger.atInfo().log(
|
||||
"Skipping registered domains export for TLD %s because Drive folder isn't specified.",
|
||||
tld);
|
||||
tldStr);
|
||||
} else {
|
||||
String resultMsg =
|
||||
driveConnection.createOrUpdateFile(
|
||||
REGISTERED_DOMAINS_FILENAME,
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
registry.getDriveFolderId(),
|
||||
tld.getDriveFolderId(),
|
||||
domains.getBytes(UTF_8));
|
||||
logger.atInfo().log(
|
||||
"Exporting registered domains succeeded for TLD %s, response was: %s", tld, resultMsg);
|
||||
"Exporting registered domains succeeded for TLD %s, response was: %s",
|
||||
tldStr, resultMsg);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
"Error exporting registered domains for TLD %s to Drive, skipping...", tld);
|
||||
"Error exporting registered domains for TLD %s to Drive, skipping...", tldStr);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.PremiumList.PremiumEntry;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.request.Action;
|
||||
@@ -61,7 +61,10 @@ public class ExportPremiumTermsAction implements Runnable {
|
||||
@Config("premiumTermsExportDisclaimer")
|
||||
String exportDisclaimer;
|
||||
|
||||
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
||||
@Inject
|
||||
@Parameter(RequestParameters.PARAM_TLD)
|
||||
String tldStr;
|
||||
|
||||
@Inject Response response;
|
||||
|
||||
@Inject
|
||||
@@ -88,59 +91,59 @@ public class ExportPremiumTermsAction implements Runnable {
|
||||
public void run() {
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
try {
|
||||
Registry registry = Registry.get(tld);
|
||||
String resultMsg = checkConfig(registry).orElseGet(() -> exportPremiumTerms(registry));
|
||||
Tld tld = Tld.get(tldStr);
|
||||
String resultMsg = checkConfig(tld).orElseGet(() -> exportPremiumTerms(tld));
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload(resultMsg);
|
||||
} catch (Throwable e) {
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(e.getMessage());
|
||||
throw new RuntimeException(
|
||||
String.format("Exception occurred while exporting premium terms for TLD %s.", tld), e);
|
||||
String.format("Exception occurred while exporting premium terms for TLD %s.", tldStr), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code registry} is properly configured to export premium terms.
|
||||
* Checks if {@link Tld} is properly configured to export premium terms.
|
||||
*
|
||||
* @return {@link Optional#empty()} if {@code registry} export may proceed. Otherwise returns an
|
||||
* error message
|
||||
* @return {@link Optional#empty()} if {@link Tld} export may proceed. Otherwise returns an error
|
||||
* message
|
||||
*/
|
||||
private Optional<String> checkConfig(Registry registry) {
|
||||
if (isNullOrEmpty(registry.getDriveFolderId())) {
|
||||
private Optional<String> checkConfig(Tld tld) {
|
||||
if (isNullOrEmpty(tld.getDriveFolderId())) {
|
||||
logger.atInfo().log(
|
||||
"Skipping premium terms export for TLD %s because Drive folder isn't specified.", tld);
|
||||
"Skipping premium terms export for TLD %s because Drive folder isn't specified.", tldStr);
|
||||
return Optional.of("Skipping export because no Drive folder is associated with this TLD");
|
||||
}
|
||||
if (!registry.getPremiumListName().isPresent()) {
|
||||
logger.atInfo().log("No premium terms to export for TLD '%s'.", tld);
|
||||
if (!tld.getPremiumListName().isPresent()) {
|
||||
logger.atInfo().log("No premium terms to export for TLD '%s'.", tldStr);
|
||||
return Optional.of("No premium lists configured");
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private String exportPremiumTerms(Registry registry) {
|
||||
private String exportPremiumTerms(Tld tld) {
|
||||
try {
|
||||
String fileId =
|
||||
driveConnection.createOrUpdateFile(
|
||||
PREMIUM_TERMS_FILENAME,
|
||||
EXPORT_MIME_TYPE,
|
||||
registry.getDriveFolderId(),
|
||||
getFormattedPremiumTerms(registry).getBytes(UTF_8));
|
||||
tld.getDriveFolderId(),
|
||||
getFormattedPremiumTerms(tld).getBytes(UTF_8));
|
||||
logger.atInfo().log(
|
||||
"Exporting premium terms succeeded for TLD %s, file ID is: %s", tld, fileId);
|
||||
"Exporting premium terms succeeded for TLD %s, file ID is: %s", tldStr, fileId);
|
||||
return fileId;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error exporting premium terms file to Drive.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getFormattedPremiumTerms(Registry registry) {
|
||||
checkState(registry.getPremiumListName().isPresent(), "%s does not have a premium list", tld);
|
||||
String premiumListName = registry.getPremiumListName().get();
|
||||
private String getFormattedPremiumTerms(Tld tld) {
|
||||
checkState(tld.getPremiumListName().isPresent(), "%s does not have a premium list", tldStr);
|
||||
String premiumListName = tld.getPremiumListName().get();
|
||||
checkState(
|
||||
PremiumListDao.getLatestRevision(premiumListName).isPresent(),
|
||||
"Could not load premium list for " + tld);
|
||||
"Could not load premium list for " + tldStr);
|
||||
SortedSet<String> premiumTerms =
|
||||
PremiumListDao.loadAllPremiumEntries(premiumListName).stream()
|
||||
.map(PremiumEntry::toString)
|
||||
|
||||
@@ -23,7 +23,7 @@ import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
@@ -46,7 +46,11 @@ public class ExportReservedTermsAction implements Runnable {
|
||||
|
||||
@Inject DriveConnection driveConnection;
|
||||
@Inject ExportUtils exportUtils;
|
||||
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
||||
|
||||
@Inject
|
||||
@Parameter(RequestParameters.PARAM_TLD)
|
||||
String tldStr;
|
||||
|
||||
@Inject Response response;
|
||||
@Inject ExportReservedTermsAction() {}
|
||||
|
||||
@@ -61,23 +65,25 @@ public class ExportReservedTermsAction implements Runnable {
|
||||
public void run() {
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
try {
|
||||
Registry registry = Registry.get(tld);
|
||||
Tld tld = Tld.get(tldStr);
|
||||
String resultMsg;
|
||||
if (registry.getReservedListNames().isEmpty() && isNullOrEmpty(registry.getDriveFolderId())) {
|
||||
if (tld.getReservedListNames().isEmpty() && isNullOrEmpty(tld.getDriveFolderId())) {
|
||||
resultMsg = "No reserved lists configured";
|
||||
logger.atInfo().log("No reserved terms to export for TLD '%s'.", tld);
|
||||
} else if (registry.getDriveFolderId() == null) {
|
||||
logger.atInfo().log("No reserved terms to export for TLD '%s'.", tldStr);
|
||||
} else if (tld.getDriveFolderId() == null) {
|
||||
resultMsg = "Skipping export because no Drive folder is associated with this TLD";
|
||||
logger.atInfo().log(
|
||||
"Skipping reserved terms export for TLD %s because Drive folder isn't specified.", tld);
|
||||
"Skipping reserved terms export for TLD %s because Drive folder isn't specified.",
|
||||
tldStr);
|
||||
} else {
|
||||
resultMsg = driveConnection.createOrUpdateFile(
|
||||
RESERVED_TERMS_FILENAME,
|
||||
EXPORT_MIME_TYPE,
|
||||
registry.getDriveFolderId(),
|
||||
exportUtils.exportReservedTerms(registry).getBytes(UTF_8));
|
||||
resultMsg =
|
||||
driveConnection.createOrUpdateFile(
|
||||
RESERVED_TERMS_FILENAME,
|
||||
EXPORT_MIME_TYPE,
|
||||
tld.getDriveFolderId(),
|
||||
exportUtils.exportReservedTerms(tld).getBytes(UTF_8));
|
||||
logger.atInfo().log(
|
||||
"Exporting reserved terms succeeded for TLD %s, response was: %s", tld, resultMsg);
|
||||
"Exporting reserved terms succeeded for TLD %s, response was: %s", tldStr, resultMsg);
|
||||
}
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload(resultMsg);
|
||||
@@ -85,7 +91,8 @@ public class ExportReservedTermsAction implements Runnable {
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(e.getMessage());
|
||||
throw new RuntimeException(
|
||||
String.format("Exception occurred while exporting reserved terms for TLD %s.", tld), e);
|
||||
String.format("Exception occurred while exporting reserved terms for TLD %s.", tldStr),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.export;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.model.tld.label.ReservedList.ReservedListEntry;
|
||||
import google.registry.model.tld.label.ReservedListDao;
|
||||
@@ -36,10 +36,10 @@ public final class ExportUtils {
|
||||
}
|
||||
|
||||
/** Returns the file contents of the auto-export reserved terms document for the given TLD. */
|
||||
public String exportReservedTerms(Registry registry) {
|
||||
public String exportReservedTerms(Tld tld) {
|
||||
StringBuilder termsBuilder = new StringBuilder(reservedTermsExportDisclaimer).append("\n");
|
||||
Set<String> reservedTerms = new TreeSet<>();
|
||||
for (String reservedListName : registry.getReservedListNames()) {
|
||||
for (String reservedListName : tld.getReservedListNames()) {
|
||||
ReservedList reservedList =
|
||||
ReservedListDao.getLatestRevision(reservedListName)
|
||||
.orElseThrow(
|
||||
|
||||
@@ -46,7 +46,7 @@ import google.registry.flows.domain.DomainFlowUtils.BadCommandForRegistryPhaseEx
|
||||
import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.monitoring.whitebox.CheckApiMetric;
|
||||
import google.registry.monitoring.whitebox.CheckApiMetric.Availability;
|
||||
@@ -116,9 +116,9 @@ public class CheckApiAction implements Runnable {
|
||||
validateDomainNameWithIdnTables(domainName);
|
||||
|
||||
DateTime now = clock.nowUtc();
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
Tld tld = Tld.get(domainName.parent().toString());
|
||||
try {
|
||||
verifyNotInPredelegation(registry, now);
|
||||
verifyNotInPredelegation(tld, now);
|
||||
} catch (BadCommandForRegistryPhaseException e) {
|
||||
metricBuilder.status(INVALID_REGISTRY_PHASE);
|
||||
return fail("Check in this TLD is not allowed in the current registry phase");
|
||||
|
||||
@@ -23,7 +23,7 @@ import google.registry.flows.domain.DomainPricingLogic;
|
||||
import google.registry.flows.domain.FeesAndCredits;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -81,7 +81,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract FeesAndCredits feesAndCredits();
|
||||
|
||||
public abstract Registry registry();
|
||||
public abstract Tld tld();
|
||||
|
||||
public abstract InternetDomainName domainName();
|
||||
|
||||
@@ -99,7 +99,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract Builder setFeesAndCredits(FeesAndCredits feesAndCredits);
|
||||
|
||||
public abstract Builder setRegistry(Registry registry);
|
||||
public abstract Builder setTld(Tld tld);
|
||||
|
||||
public abstract Builder setDomainName(InternetDomainName domainName);
|
||||
|
||||
@@ -117,7 +117,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract FeesAndCredits feesAndCredits();
|
||||
|
||||
public abstract Registry registry();
|
||||
public abstract Tld tld();
|
||||
|
||||
public abstract InternetDomainName domainName();
|
||||
|
||||
@@ -135,7 +135,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract Builder setFeesAndCredits(FeesAndCredits feesAndCredits);
|
||||
|
||||
public abstract Builder setRegistry(Registry registry);
|
||||
public abstract Builder setTld(Tld tld);
|
||||
|
||||
public abstract Builder setDomainName(InternetDomainName domainName);
|
||||
|
||||
@@ -153,7 +153,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract FeesAndCredits feesAndCredits();
|
||||
|
||||
public abstract Registry registry();
|
||||
public abstract Tld tld();
|
||||
|
||||
public abstract InternetDomainName domainName();
|
||||
|
||||
@@ -169,7 +169,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract Builder setFeesAndCredits(FeesAndCredits feesAndCredits);
|
||||
|
||||
public abstract Builder setRegistry(Registry registry);
|
||||
public abstract Builder setTld(Tld tld);
|
||||
|
||||
public abstract Builder setDomainName(InternetDomainName domainName);
|
||||
|
||||
@@ -185,7 +185,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract FeesAndCredits feesAndCredits();
|
||||
|
||||
public abstract Registry registry();
|
||||
public abstract Tld tld();
|
||||
|
||||
public abstract InternetDomainName domainName();
|
||||
|
||||
@@ -201,7 +201,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract Builder setFeesAndCredits(FeesAndCredits feesAndCredits);
|
||||
|
||||
public abstract Builder setRegistry(Registry registry);
|
||||
public abstract Builder setTld(Tld tld);
|
||||
|
||||
public abstract Builder setDomainName(InternetDomainName domainName);
|
||||
|
||||
@@ -217,7 +217,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract FeesAndCredits feesAndCredits();
|
||||
|
||||
public abstract Registry registry();
|
||||
public abstract Tld tld();
|
||||
|
||||
public abstract InternetDomainName domainName();
|
||||
|
||||
@@ -233,7 +233,7 @@ public class DomainPricingCustomLogic extends BaseFlowCustomLogic {
|
||||
|
||||
public abstract Builder setFeesAndCredits(FeesAndCredits feesAndCredits);
|
||||
|
||||
public abstract Builder setRegistry(Registry registry);
|
||||
public abstract Builder setTld(Tld tld);
|
||||
|
||||
public abstract Builder setDomainName(InternetDomainName domainName);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import static google.registry.flows.domain.DomainFlowUtils.isValidReservedCreate
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPredelegation;
|
||||
import static google.registry.model.tld.Registry.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.model.tld.label.ReservationType.getTypeOfHighestSeverity;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
@@ -38,7 +38,6 @@ 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;
|
||||
@@ -53,6 +52,11 @@ import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponsePar
|
||||
import google.registry.flows.custom.DomainCheckFlowCustomLogic.BeforeResponseReturnData;
|
||||
import google.registry.flows.domain.token.AllocationTokenDomainCheckResults;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotInPromotionException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForCommandException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForDomainException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForTldException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.ForeignKeyUtils;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
@@ -73,8 +77,8 @@ import google.registry.model.eppoutput.CheckData.DomainCheckData;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Clock;
|
||||
@@ -117,7 +121,6 @@ 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;
|
||||
@@ -161,7 +164,7 @@ public final class DomainCheckFlow implements Flow {
|
||||
if (tldFirstTimeSeen && !isSuperuser) {
|
||||
checkAllowedAccessToTld(registrarId, tld);
|
||||
checkHasBillingAccount(registrarId, tld);
|
||||
verifyNotInPredelegation(Registry.get(tld), now);
|
||||
verifyNotInPredelegation(Tld.get(tld), now);
|
||||
}
|
||||
}
|
||||
ImmutableMap<String, InternetDomainName> parsedDomains = parsedDomainsBuilder.build();
|
||||
@@ -187,7 +190,7 @@ public final class DomainCheckFlow implements Flow {
|
||||
ImmutableList.Builder<DomainCheck> checksBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableSet.Builder<String> availableDomains = new ImmutableSet.Builder<>();
|
||||
ImmutableMap<String, TldState> tldStates =
|
||||
Maps.toMap(seenTlds, tld -> Registry.get(tld).getTldState(now));
|
||||
Maps.toMap(seenTlds, tld -> Tld.get(tld).getTldState(now));
|
||||
ImmutableMap<InternetDomainName, String> domainCheckResults =
|
||||
tokenDomainCheckResults
|
||||
.map(AllocationTokenDomainCheckResults::domainCheckResults)
|
||||
@@ -275,24 +278,54 @@ public final class DomainCheckFlow implements Flow {
|
||||
for (String domainName : getDomainNamesToCheckForFee(feeCheckItem, domainNames.keySet())) {
|
||||
Optional<AllocationToken> defaultToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
Registry.get(InternetDomainName.from(domainName).parent().toString()),
|
||||
Tld.get(InternetDomainName.from(domainName).parent().toString()),
|
||||
domainName,
|
||||
feeCheckItem.getCommandName(),
|
||||
registrarId,
|
||||
now);
|
||||
FeeCheckResponseExtensionItem.Builder<?> builder = feeCheckItem.createResponseBuilder();
|
||||
Optional<Domain> domain = Optional.ofNullable(domainObjs.get(domainName));
|
||||
handleFeeRequest(
|
||||
feeCheckItem,
|
||||
builder,
|
||||
domainNames.get(domainName),
|
||||
domain,
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
allocationToken.isPresent() ? allocationToken : defaultToken,
|
||||
availableDomains.contains(domainName),
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
responseItems.add(builder.setDomainNameIfSupported(domainName).build());
|
||||
try {
|
||||
if (allocationToken.isPresent()) {
|
||||
AllocationTokenFlowUtils.validateToken(
|
||||
InternetDomainName.from(domainName),
|
||||
allocationToken.get(),
|
||||
feeCheckItem.getCommandName(),
|
||||
registrarId,
|
||||
now);
|
||||
}
|
||||
handleFeeRequest(
|
||||
feeCheckItem,
|
||||
builder,
|
||||
domainNames.get(domainName),
|
||||
domain,
|
||||
feeCheck.getCurrency(),
|
||||
now,
|
||||
pricingLogic,
|
||||
allocationToken.isPresent() ? allocationToken : defaultToken,
|
||||
availableDomains.contains(domainName),
|
||||
recurrences.getOrDefault(domainName, null));
|
||||
responseItems.add(builder.setDomainNameIfSupported(domainName).build());
|
||||
} catch (AllocationTokenNotValidForCommandException
|
||||
| AllocationTokenNotValidForDomainException
|
||||
| AllocationTokenNotValidForRegistrarException
|
||||
| AllocationTokenNotValidForTldException
|
||||
| AllocationTokenNotInPromotionException e) {
|
||||
// Allocation token is either not an active token or it is not valid for the EPP command,
|
||||
// registrar, domain, or TLD.
|
||||
Tld tld = Tld.get(InternetDomainName.from(domainName).parent().toString());
|
||||
responseItems.add(
|
||||
builder
|
||||
.setDomainNameIfSupported(domainName)
|
||||
.setPeriod(feeCheckItem.getPeriod())
|
||||
.setCommand(
|
||||
feeCheckItem.getCommandName(),
|
||||
feeCheckItem.getPhase(),
|
||||
feeCheckItem.getSubphase())
|
||||
.setCurrencyIfSupported(tld.getCurrency())
|
||||
.setClass("token-not-supported")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ImmutableList.of(feeCheck.createResponse(responseItems.build()));
|
||||
|
||||
@@ -45,7 +45,7 @@ import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tmch.ClaimsListDao;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.HashSet;
|
||||
@@ -96,16 +96,16 @@ public final class DomainClaimsCheckFlow implements Flow {
|
||||
for (String domainName : ImmutableSet.copyOf(domainNames)) {
|
||||
InternetDomainName parsedDomain = validateDomainName(domainName);
|
||||
validateDomainNameWithIdnTables(parsedDomain);
|
||||
String tld = parsedDomain.parent().toString();
|
||||
String tldStr = parsedDomain.parent().toString();
|
||||
// Only validate access to a TLD the first time it is encountered.
|
||||
if (seenTlds.add(tld)) {
|
||||
if (seenTlds.add(tldStr)) {
|
||||
if (!isSuperuser) {
|
||||
checkAllowedAccessToTld(registrarId, tld);
|
||||
checkHasBillingAccount(registrarId, tld);
|
||||
Registry registry = Registry.get(tld);
|
||||
checkAllowedAccessToTld(registrarId, tldStr);
|
||||
checkHasBillingAccount(registrarId, tldStr);
|
||||
Tld tld = Tld.get(tldStr);
|
||||
DateTime now = clock.nowUtc();
|
||||
verifyNotInPredelegation(registry, now);
|
||||
verifyClaimsPeriodNotEnded(registry, now);
|
||||
verifyNotInPredelegation(tld, now);
|
||||
verifyClaimsPeriodNotEnded(tld, now);
|
||||
}
|
||||
}
|
||||
Optional<String> claimKey = ClaimsListDao.get().getClaimKey(parsedDomain.parts().get(0));
|
||||
|
||||
@@ -47,9 +47,9 @@ import static google.registry.model.EppResourceUtils.createDomainRepoId;
|
||||
import static google.registry.model.IdService.allocateId;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_HOLD;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
import static google.registry.model.tld.Registry.TldState.GENERAL_AVAILABILITY;
|
||||
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.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
|
||||
import static google.registry.model.tld.Tld.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.DateTimeUtils.END_OF_TIME;
|
||||
@@ -89,6 +89,7 @@ import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.FeeCreateCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
@@ -111,9 +112,9 @@ import google.registry.model.reporting.DomainTransactionRecord.TransactionReport
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.model.tmch.ClaimsList;
|
||||
import google.registry.model.tmch.ClaimsListDao;
|
||||
@@ -252,9 +253,9 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
InternetDomainName domainName = validateDomainName(command.getDomainName());
|
||||
String domainLabel = domainName.parts().get(0);
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
validateCreateCommandContactsAndNameservers(command, registry, domainName);
|
||||
TldState tldState = registry.getTldState(now);
|
||||
Tld tld = Tld.get(domainName.parent().toString());
|
||||
validateCreateCommandContactsAndNameservers(command, tld, domainName);
|
||||
TldState tldState = tld.getTldState(now);
|
||||
Optional<LaunchCreateExtension> launchCreate =
|
||||
eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
boolean hasSignedMarks =
|
||||
@@ -269,14 +270,15 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenCreateIfPresent(
|
||||
command,
|
||||
registry,
|
||||
tld,
|
||||
registrarId,
|
||||
now,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (!allocationToken.isPresent() && !registry.getDefaultPromoTokens().isEmpty()) {
|
||||
if (!allocationToken.isPresent()) {
|
||||
allocationToken =
|
||||
DomainFlowUtils.checkForDefaultToken(registry, command.getDomainName(), registrarId, now);
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
tld, command.getDomainName(), CommandName.CREATE, registrarId, now);
|
||||
if (allocationToken.isPresent()) {
|
||||
defaultTokenUsed = true;
|
||||
}
|
||||
@@ -289,12 +291,12 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
// notice without specifying a claims key, ignore the registry phase, and override blocks on
|
||||
// registering premium domains.
|
||||
if (!isSuperuser) {
|
||||
checkAllowedAccessToTld(registrarId, registry.getTldStr());
|
||||
checkHasBillingAccount(registrarId, registry.getTldStr());
|
||||
checkAllowedAccessToTld(registrarId, tld.getTldStr());
|
||||
checkHasBillingAccount(registrarId, tld.getTldStr());
|
||||
boolean isValidReservedCreate = isValidReservedCreate(domainName, allocationToken);
|
||||
ClaimsList claimsList = ClaimsListDao.get();
|
||||
verifyIsGaOrSpecialCase(
|
||||
registry,
|
||||
tld,
|
||||
claimsList,
|
||||
now,
|
||||
domainLabel,
|
||||
@@ -303,15 +305,15 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
isValidReservedCreate,
|
||||
hasSignedMarks);
|
||||
if (launchCreate.isPresent()) {
|
||||
verifyLaunchPhaseMatchesRegistryPhase(registry, launchCreate.get(), now);
|
||||
verifyLaunchPhaseMatchesRegistryPhase(tld, launchCreate.get(), now);
|
||||
}
|
||||
if (!isAnchorTenant && !isValidReservedCreate) {
|
||||
verifyNotReserved(domainName, isSunriseCreate);
|
||||
}
|
||||
if (hasClaimsNotice) {
|
||||
verifyClaimsPeriodNotEnded(registry, now);
|
||||
verifyClaimsPeriodNotEnded(tld, now);
|
||||
}
|
||||
if (now.isBefore(registry.getClaimsPeriodEnd())) {
|
||||
if (now.isBefore(tld.getClaimsPeriodEnd())) {
|
||||
verifyClaimsNoticeIfAndOnlyIfNeeded(
|
||||
domainName, claimsList, hasSignedMarks, hasClaimsNotice);
|
||||
}
|
||||
@@ -336,20 +338,19 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
Optional<FeeCreateCommandExtension> feeCreate =
|
||||
eppInput.getSingleExtension(FeeCreateCommandExtension.class);
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getCreatePrice(
|
||||
registry, targetId, now, years, isAnchorTenant, allocationToken);
|
||||
pricingLogic.getCreatePrice(tld, targetId, now, years, isAnchorTenant, allocationToken);
|
||||
validateFeeChallenge(feeCreate, feesAndCredits, defaultTokenUsed);
|
||||
Optional<SecDnsCreateExtension> secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
DateTime registrationExpirationTime = leapSafeAddYears(now, years);
|
||||
String repoId = createDomainRepoId(allocateId(), registry.getTldStr());
|
||||
String repoId = createDomainRepoId(allocateId(), tld.getTldStr());
|
||||
long historyRevisionId = allocateId();
|
||||
HistoryEntryId domainHistoryId = new HistoryEntryId(repoId, historyRevisionId);
|
||||
historyBuilder.setRevisionId(historyRevisionId);
|
||||
// Bill for the create.
|
||||
BillingEvent.OneTime createBillingEvent =
|
||||
createOneTimeBillingEvent(
|
||||
registry,
|
||||
tld,
|
||||
isAnchorTenant,
|
||||
isSunriseCreate,
|
||||
isReserved(domainName, isSunriseCreate),
|
||||
@@ -412,7 +413,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
domain.asBuilder().setCurrentPackageToken(allocationToken.get().createVKey()).build();
|
||||
}
|
||||
DomainHistory domainHistory =
|
||||
buildDomainHistory(domain, registry, now, period, registry.getAddGracePeriodLength());
|
||||
buildDomainHistory(domain, tld, now, period, tld.getAddGracePeriodLength());
|
||||
if (reservationTypes.contains(NAME_COLLISION)) {
|
||||
entitiesToSave.add(
|
||||
createNameCollisionOneTimePollMessage(targetId, domainHistory, registrarId, now));
|
||||
@@ -491,7 +492,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
* non-superusers.
|
||||
*/
|
||||
private void verifyIsGaOrSpecialCase(
|
||||
Registry registry,
|
||||
Tld tld,
|
||||
ClaimsList claimsList,
|
||||
DateTime now,
|
||||
String domainLabel,
|
||||
@@ -503,7 +504,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
MustHaveSignedMarksInCurrentPhaseException,
|
||||
NoTrademarkedRegistrationsBeforeSunriseException {
|
||||
// We allow general registration during GA.
|
||||
TldState currentState = registry.getTldState(now);
|
||||
TldState currentState = tld.getTldState(now);
|
||||
if (currentState.equals(GENERAL_AVAILABILITY)) {
|
||||
return;
|
||||
}
|
||||
@@ -524,7 +525,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
// Trademarked domains cannot be registered until after the sunrise period has ended, unless
|
||||
// a valid signed mark is provided. Signed marks can only be provided during sunrise.
|
||||
// Thus, when bypassing TLD state checks, a post-sunrise state is always fine.
|
||||
if (registry.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
|
||||
if (tld.getTldStateTransitions().headMap(now).containsValue(START_DATE_SUNRISE)) {
|
||||
return;
|
||||
} else {
|
||||
// If sunrise hasn't happened yet, trademarked domains are unavailable
|
||||
@@ -557,23 +558,22 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
}
|
||||
|
||||
private DomainHistory buildDomainHistory(
|
||||
Domain domain, Registry registry, DateTime now, Period period, Duration addGracePeriod) {
|
||||
Domain domain, Tld tld, DateTime now, Period period, Duration addGracePeriod) {
|
||||
// We ignore prober transactions
|
||||
if (registry.getTldType() == TldType.REAL) {
|
||||
historyBuilder
|
||||
.setDomainTransactionRecords(
|
||||
ImmutableSet.of(
|
||||
DomainTransactionRecord.create(
|
||||
registry.getTldStr(),
|
||||
now.plus(addGracePeriod),
|
||||
TransactionReportField.netAddsFieldFromYears(period.getValue()),
|
||||
1)));
|
||||
if (tld.getTldType() == TldType.REAL) {
|
||||
historyBuilder.setDomainTransactionRecords(
|
||||
ImmutableSet.of(
|
||||
DomainTransactionRecord.create(
|
||||
tld.getTldStr(),
|
||||
now.plus(addGracePeriod),
|
||||
TransactionReportField.netAddsFieldFromYears(period.getValue()),
|
||||
1)));
|
||||
}
|
||||
return historyBuilder.setType(DOMAIN_CREATE).setPeriod(period).setDomain(domain).build();
|
||||
}
|
||||
|
||||
private BillingEvent.OneTime createOneTimeBillingEvent(
|
||||
Registry registry,
|
||||
Tld tld,
|
||||
boolean isAnchorTenant,
|
||||
boolean isSunriseCreate,
|
||||
boolean isReserved,
|
||||
@@ -605,8 +605,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
.setBillingTime(
|
||||
now.plus(
|
||||
isAnchorTenant
|
||||
? registry.getAnchorTenantAddGracePeriodLength()
|
||||
: registry.getAddGracePeriodLength()))
|
||||
? tld.getAnchorTenantAddGracePeriodLength()
|
||||
: tld.getAddGracePeriodLength()))
|
||||
.setFlags(flagsBuilder.build())
|
||||
.setDomainHistoryId(domainHistoryId)
|
||||
.build();
|
||||
|
||||
@@ -88,8 +88,8 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
@@ -146,8 +146,8 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
// Loads the target resource if it exists
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
Registry registry = Registry.get(existingDomain.getTld());
|
||||
verifyDeleteAllowed(existingDomain, registry, now);
|
||||
Tld tld = Tld.get(existingDomain.getTld());
|
||||
verifyDeleteAllowed(existingDomain, tld, now);
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder().setExistingDomain(existingDomain).build());
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
@@ -160,8 +160,8 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
builder = existingDomain.asBuilder();
|
||||
}
|
||||
builder.setLastEppUpdateTime(now).setLastEppUpdateRegistrarId(registrarId);
|
||||
Duration redemptionGracePeriodLength = registry.getRedemptionGracePeriodLength();
|
||||
Duration pendingDeleteLength = registry.getPendingDeleteLength();
|
||||
Duration redemptionGracePeriodLength = tld.getRedemptionGracePeriodLength();
|
||||
Duration pendingDeleteLength = tld.getPendingDeleteLength();
|
||||
Optional<DomainDeleteSuperuserExtension> domainDeleteSuperuserExtension =
|
||||
eppInput.getSingleExtension(DomainDeleteSuperuserExtension.class);
|
||||
if (domainDeleteSuperuserExtension.isPresent()) {
|
||||
@@ -249,7 +249,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
|
||||
Domain newDomain = builder.build();
|
||||
DomainHistory domainHistory =
|
||||
buildDomainHistory(newDomain, registry, now, durationUntilDelete, inAddGracePeriod);
|
||||
buildDomainHistory(newDomain, tld, now, durationUntilDelete, inAddGracePeriod);
|
||||
handlePendingTransferOnDelete(existingDomain, newDomain, now, domainHistory);
|
||||
// Close the autorenew billing event and poll message. This may delete the poll message. Store
|
||||
// the updated recurring billing event, we'll need it later and can't reload it.
|
||||
@@ -289,14 +289,14 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
.build();
|
||||
}
|
||||
|
||||
private void verifyDeleteAllowed(Domain existingDomain, Registry registry, DateTime now)
|
||||
private void verifyDeleteAllowed(Domain existingDomain, Tld tld, DateTime now)
|
||||
throws EppException {
|
||||
verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES);
|
||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||
if (!isSuperuser) {
|
||||
verifyResourceOwnership(registrarId, existingDomain);
|
||||
verifyNotInPredelegation(registry, now);
|
||||
checkAllowedAccessToTld(registrarId, registry.getTld().toString());
|
||||
verifyNotInPredelegation(tld, now);
|
||||
checkAllowedAccessToTld(registrarId, tld.getTld().toString());
|
||||
}
|
||||
if (!existingDomain.getSubordinateHosts().isEmpty()) {
|
||||
throw new DomainToDeleteHasHostsException();
|
||||
@@ -305,17 +305,18 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
|
||||
private DomainHistory buildDomainHistory(
|
||||
Domain domain,
|
||||
Registry registry,
|
||||
Tld tld,
|
||||
DateTime now,
|
||||
Duration durationUntilDelete,
|
||||
boolean inAddGracePeriod) {
|
||||
// We ignore prober transactions
|
||||
if (registry.getTldType() == TldType.REAL) {
|
||||
Duration maxGracePeriod = Collections.max(
|
||||
ImmutableSet.of(
|
||||
registry.getAddGracePeriodLength(),
|
||||
registry.getAutoRenewGracePeriodLength(),
|
||||
registry.getRenewGracePeriodLength()));
|
||||
if (tld.getTldType() == TldType.REAL) {
|
||||
Duration maxGracePeriod =
|
||||
Collections.max(
|
||||
ImmutableSet.of(
|
||||
tld.getAddGracePeriodLength(),
|
||||
tld.getAutoRenewGracePeriodLength(),
|
||||
tld.getRenewGracePeriodLength()));
|
||||
ImmutableSet<DomainTransactionRecord> cancelledRecords =
|
||||
createCancelingRecords(
|
||||
domain,
|
||||
|
||||
@@ -28,10 +28,10 @@ import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
|
||||
import static google.registry.model.tld.Registries.findTldForName;
|
||||
import static google.registry.model.tld.Registries.getTlds;
|
||||
import static google.registry.model.tld.Registry.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Registry.TldState.PREDELEGATION;
|
||||
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.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
|
||||
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.model.tld.label.ReservationType.ALLOWED_IN_SUNRISE;
|
||||
import static google.registry.model.tld.label.ReservationType.FULLY_BLOCKED;
|
||||
import static google.registry.model.tld.label.ReservationType.NAME_COLLISION;
|
||||
@@ -97,6 +97,7 @@ import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Credit;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.fee.FeeQueryResponseExtensionItem;
|
||||
import google.registry.model.domain.fee.FeeTransformCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
@@ -123,9 +124,9 @@ import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.label.ReservationType;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.model.tmch.ClaimsList;
|
||||
@@ -175,8 +176,7 @@ public class DomainFlowUtils {
|
||||
CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9').or(CharMatcher.anyOf("-.")));
|
||||
|
||||
/** Default validator used to determine if an IDN name can be provisioned on a TLD. */
|
||||
private static final IdnLabelValidator IDN_LABEL_VALIDATOR =
|
||||
IdnLabelValidator.createDefaultIdnLabelValidator();
|
||||
private static final IdnLabelValidator IDN_LABEL_VALIDATOR = new IdnLabelValidator();
|
||||
|
||||
/** The maximum number of DS records allowed on a domain. */
|
||||
private static final int MAX_DS_RECORDS_PER_DOMAIN = 8;
|
||||
@@ -302,17 +302,17 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
/** Check if the registrar has the correct billing account map configured. */
|
||||
public static void checkHasBillingAccount(String registrarId, String tld) throws EppException {
|
||||
Registry registry = Registry.get(tld);
|
||||
public static void checkHasBillingAccount(String registrarId, String tldStr) throws EppException {
|
||||
Tld tld = Tld.get(tldStr);
|
||||
// Don't enforce the billing account check on test (i.e. prober/OT&E) TLDs.
|
||||
if (registry.getTldType() == TldType.TEST) {
|
||||
if (tld.getTldType() == TldType.TEST) {
|
||||
return;
|
||||
}
|
||||
if (!Registrar.loadByRegistrarIdCached(registrarId)
|
||||
.get()
|
||||
.getBillingAccountMap()
|
||||
.containsKey(registry.getCurrency())) {
|
||||
throw new DomainFlowUtils.MissingBillingAccountMapException(registry.getCurrency());
|
||||
.containsKey(tld.getCurrency())) {
|
||||
throw new DomainFlowUtils.MissingBillingAccountMapException(tld.getCurrency());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,8 +412,7 @@ public class DomainFlowUtils {
|
||||
static void validateNameserversCountForTld(String tld, InternetDomainName domainName, int count)
|
||||
throws EppException {
|
||||
// For TLDs with a nameserver allow list, all domains must have at least 1 nameserver.
|
||||
ImmutableSet<String> tldNameserversAllowList =
|
||||
Registry.get(tld).getAllowedFullyQualifiedHostNames();
|
||||
ImmutableSet<String> tldNameserversAllowList = Tld.get(tld).getAllowedFullyQualifiedHostNames();
|
||||
if (!tldNameserversAllowList.isEmpty() && count == 0) {
|
||||
throw new NameserversNotSpecifiedForTldWithNameserverAllowListException(
|
||||
domainName.toString());
|
||||
@@ -470,7 +469,7 @@ public class DomainFlowUtils {
|
||||
|
||||
static void validateRegistrantAllowedOnTld(String tld, String registrantContactId)
|
||||
throws RegistrantNotAllowedException {
|
||||
ImmutableSet<String> allowedRegistrants = Registry.get(tld).getAllowedRegistrantContactIds();
|
||||
ImmutableSet<String> allowedRegistrants = Tld.get(tld).getAllowedRegistrantContactIds();
|
||||
// Empty allow list or null registrantContactId are ignored.
|
||||
if (registrantContactId != null
|
||||
&& !allowedRegistrants.isEmpty()
|
||||
@@ -481,7 +480,7 @@ public class DomainFlowUtils {
|
||||
|
||||
static void validateNameserversAllowedOnTld(String tld, Set<String> fullyQualifiedHostNames)
|
||||
throws EppException {
|
||||
ImmutableSet<String> allowedHostNames = Registry.get(tld).getAllowedFullyQualifiedHostNames();
|
||||
ImmutableSet<String> allowedHostNames = Tld.get(tld).getAllowedFullyQualifiedHostNames();
|
||||
Set<String> hostnames = nullToEmpty(fullyQualifiedHostNames);
|
||||
if (!allowedHostNames.isEmpty()) { // Empty allow list is ignored.
|
||||
Set<String> disallowedNameservers = difference(hostnames, allowedHostNames);
|
||||
@@ -514,13 +513,13 @@ public class DomainFlowUtils {
|
||||
domainName.parts().get(0), domainName.parent().toString());
|
||||
}
|
||||
|
||||
/** Verifies that a launch extension's specified phase matches the specified registry's phase. */
|
||||
/** Verifies that a launch extension's specified phase matches the specified tld's phase. */
|
||||
static void verifyLaunchPhaseMatchesRegistryPhase(
|
||||
Registry registry, LaunchExtension launchExtension, DateTime now) throws EppException {
|
||||
Tld tld, LaunchExtension launchExtension, DateTime now) throws EppException {
|
||||
if (!LAUNCH_PHASE_TO_TLD_STATES.containsKey(launchExtension.getPhase())
|
||||
|| !LAUNCH_PHASE_TO_TLD_STATES
|
||||
.get(launchExtension.getPhase())
|
||||
.contains(registry.getTldState(now))) {
|
||||
.contains(tld.getTldState(now))) {
|
||||
// No launch operations are allowed during the quiet period or predelegation.
|
||||
throw new LaunchPhaseMismatchException();
|
||||
}
|
||||
@@ -652,9 +651,9 @@ public class DomainFlowUtils {
|
||||
builder.setEffectiveDateIfSupported(now);
|
||||
}
|
||||
String domainNameString = domainName.toString();
|
||||
Registry registry = Registry.get(domainName.parent().toString());
|
||||
Tld tld = Tld.get(domainName.parent().toString());
|
||||
int years = verifyUnitIsYears(feeRequest.getPeriod()).getValue();
|
||||
boolean isSunrise = (registry.getTldState(now) == START_DATE_SUNRISE);
|
||||
boolean isSunrise = (tld.getTldState(now) == START_DATE_SUNRISE);
|
||||
|
||||
if (feeRequest.getPhase() != null || feeRequest.getSubphase() != null) {
|
||||
throw new FeeChecksDontSupportPhasesException();
|
||||
@@ -662,20 +661,18 @@ public class DomainFlowUtils {
|
||||
|
||||
CurrencyUnit currency =
|
||||
feeRequest.getCurrency() != null ? feeRequest.getCurrency() : topLevelCurrency;
|
||||
if ((currency != null) && !currency.equals(registry.getCurrency())) {
|
||||
if ((currency != null) && !currency.equals(tld.getCurrency())) {
|
||||
throw new CurrencyUnitMismatchException();
|
||||
}
|
||||
|
||||
builder
|
||||
.setCommand(feeRequest.getCommandName(), feeRequest.getPhase(), feeRequest.getSubphase())
|
||||
.setCurrencyIfSupported(registry.getCurrency())
|
||||
.setCurrencyIfSupported(tld.getCurrency())
|
||||
.setPeriod(feeRequest.getPeriod());
|
||||
|
||||
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) {
|
||||
@@ -687,7 +684,7 @@ public class DomainFlowUtils {
|
||||
fees =
|
||||
pricingLogic
|
||||
.getCreatePrice(
|
||||
registry,
|
||||
tld,
|
||||
domainNameString,
|
||||
now,
|
||||
years,
|
||||
@@ -701,7 +698,7 @@ public class DomainFlowUtils {
|
||||
fees =
|
||||
pricingLogic
|
||||
.getRenewPrice(
|
||||
registry, domainNameString, now, years, recurringBillingEvent, allocationToken)
|
||||
tld, domainNameString, now, years, recurringBillingEvent, allocationToken)
|
||||
.getFees();
|
||||
break;
|
||||
case RESTORE:
|
||||
@@ -719,7 +716,7 @@ public class DomainFlowUtils {
|
||||
// restore because they can't be restored in the first place.
|
||||
boolean isExpired =
|
||||
domain.isPresent() && domain.get().getRegistrationExpirationTime().isBefore(now);
|
||||
fees = pricingLogic.getRestorePrice(registry, domainNameString, now, isExpired).getFees();
|
||||
fees = pricingLogic.getRestorePrice(tld, domainNameString, now, isExpired).getFees();
|
||||
break;
|
||||
case TRANSFER:
|
||||
if (years != 1) {
|
||||
@@ -728,12 +725,12 @@ public class DomainFlowUtils {
|
||||
builder.setAvailIfSupported(true);
|
||||
fees =
|
||||
pricingLogic
|
||||
.getTransferPrice(registry, domainNameString, now, recurringBillingEvent)
|
||||
.getTransferPrice(tld, domainNameString, now, recurringBillingEvent)
|
||||
.getFees();
|
||||
break;
|
||||
case UPDATE:
|
||||
builder.setAvailIfSupported(true);
|
||||
fees = pricingLogic.getUpdatePrice(registry, domainNameString, now).getFees();
|
||||
fees = pricingLogic.getUpdatePrice(tld, domainNameString, now).getFees();
|
||||
break;
|
||||
default:
|
||||
throw new UnknownFeeCommandException(feeRequest.getUnparsedCommandName());
|
||||
@@ -744,7 +741,7 @@ public class DomainFlowUtils {
|
||||
// are returning any premium fees, but only if the fee class isn't already set (i.e. because
|
||||
// the domain is reserved, which overrides any other classes).
|
||||
boolean isNameCollisionInSunrise =
|
||||
registry.getTldState(now).equals(START_DATE_SUNRISE)
|
||||
tld.getTldState(now).equals(START_DATE_SUNRISE)
|
||||
&& getReservationTypes(domainName).contains(NAME_COLLISION);
|
||||
boolean isPremium = fees.stream().anyMatch(BaseFee::isPremium);
|
||||
feeClass =
|
||||
@@ -995,7 +992,7 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
/** Check that the registry phase is not predelegation, during which some flows are forbidden. */
|
||||
public static void verifyNotInPredelegation(Registry registry, DateTime now)
|
||||
public static void verifyNotInPredelegation(Tld registry, DateTime now)
|
||||
throws BadCommandForRegistryPhaseException {
|
||||
if (registry.getTldState(now) == PREDELEGATION) {
|
||||
throw new BadCommandForRegistryPhaseException();
|
||||
@@ -1004,17 +1001,17 @@ public class DomainFlowUtils {
|
||||
|
||||
/** Validate the contacts and nameservers specified in a domain create command. */
|
||||
static void validateCreateCommandContactsAndNameservers(
|
||||
Create command, Registry registry, InternetDomainName domainName) throws EppException {
|
||||
Create command, Tld tld, InternetDomainName domainName) throws EppException {
|
||||
verifyNotInPendingDelete(
|
||||
command.getContacts(), command.getRegistrant(), command.getNameservers());
|
||||
validateContactsHaveTypes(command.getContacts());
|
||||
String tld = registry.getTldStr();
|
||||
validateRegistrantAllowedOnTld(tld, command.getRegistrantContactId());
|
||||
String tldStr = tld.getTldStr();
|
||||
validateRegistrantAllowedOnTld(tldStr, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
|
||||
ImmutableSet<String> hostNames = command.getNameserverHostNames();
|
||||
validateNameserversCountForTld(tld, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tld, hostNames);
|
||||
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tldStr, hostNames);
|
||||
}
|
||||
|
||||
/** Validate the secDNS extension, if present. */
|
||||
@@ -1063,10 +1060,9 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
/** Check that the claims period hasn't ended. */
|
||||
static void verifyClaimsPeriodNotEnded(Registry registry, DateTime now)
|
||||
throws ClaimsPeriodEndedException {
|
||||
if (isAtOrAfter(now, registry.getClaimsPeriodEnd())) {
|
||||
throw new ClaimsPeriodEndedException(registry.getTldStr());
|
||||
static void verifyClaimsPeriodNotEnded(Tld tld, DateTime now) throws ClaimsPeriodEndedException {
|
||||
if (isAtOrAfter(now, tld.getClaimsPeriodEnd())) {
|
||||
throw new ClaimsPeriodEndedException(tld.getTldStr());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1204,14 +1200,15 @@ public class DomainFlowUtils {
|
||||
* 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())) {
|
||||
Tld tld, String domainName, CommandName commandName, String registrarId, DateTime now)
|
||||
throws EppException {
|
||||
if (isNullOrEmpty(tld.getDefaultPromoTokens())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Map<VKey<AllocationToken>, Optional<AllocationToken>> tokens =
|
||||
AllocationToken.getAll(registry.getDefaultPromoTokens());
|
||||
AllocationToken.getAll(tld.getDefaultPromoTokens());
|
||||
ImmutableList<Optional<AllocationToken>> tokenList =
|
||||
registry.getDefaultPromoTokens().stream()
|
||||
tld.getDefaultPromoTokens().stream()
|
||||
.map(tokens::get)
|
||||
.filter(Optional::isPresent)
|
||||
.collect(toImmutableList());
|
||||
@@ -1222,7 +1219,7 @@ public class DomainFlowUtils {
|
||||
for (Optional<AllocationToken> token : tokenList) {
|
||||
try {
|
||||
AllocationTokenFlowUtils.validateToken(
|
||||
InternetDomainName.from(domainName), token.get(), registrarId, now);
|
||||
InternetDomainName.from(domainName), token.get(), commandName, registrarId, now);
|
||||
} catch (AssociationProhibitsOperationException | StatusProhibitsOperationException e) {
|
||||
// Allocation token was not valid for this registration, continue to check the next token in
|
||||
// the list
|
||||
|
||||
@@ -36,7 +36,7 @@ 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 google.registry.model.tld.Tld;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -66,14 +66,14 @@ public final class DomainPricingLogic {
|
||||
* applied to the first year.
|
||||
*/
|
||||
FeesAndCredits getCreatePrice(
|
||||
Registry registry,
|
||||
Tld tld,
|
||||
String domainName,
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
boolean isAnchorTenant,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws EppException {
|
||||
CurrencyUnit currency = registry.getCurrency();
|
||||
CurrencyUnit currency = tld.getCurrency();
|
||||
|
||||
BaseFee createFeeOrCredit;
|
||||
// Domain create cost is always zero for anchor tenants
|
||||
@@ -88,7 +88,7 @@ public final class DomainPricingLogic {
|
||||
}
|
||||
|
||||
// Create fees for the cost and the EAP fee, if any.
|
||||
Fee eapFee = registry.getEapFeeFor(dateTime);
|
||||
Fee eapFee = tld.getEapFeeFor(dateTime);
|
||||
FeesAndCredits.Builder feesBuilder =
|
||||
new FeesAndCredits.Builder().setCurrency(currency).addFeeOrCredit(createFeeOrCredit);
|
||||
// Don't charge anchor tenants EAP fees.
|
||||
@@ -100,7 +100,7 @@ public final class DomainPricingLogic {
|
||||
return customLogic.customizeCreatePrice(
|
||||
CreatePriceParameters.newBuilder()
|
||||
.setFeesAndCredits(feesBuilder.build())
|
||||
.setRegistry(registry)
|
||||
.setTld(tld)
|
||||
.setDomainName(InternetDomainName.from(domainName))
|
||||
.setAsOfDate(dateTime)
|
||||
.setYears(years)
|
||||
@@ -109,7 +109,7 @@ public final class DomainPricingLogic {
|
||||
|
||||
/** Returns a new renewal cost for the pricer. */
|
||||
public FeesAndCredits getRenewPrice(
|
||||
Registry registry,
|
||||
Tld tld,
|
||||
String domainName,
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
@@ -150,7 +150,7 @@ public final class DomainPricingLogic {
|
||||
false,
|
||||
years,
|
||||
allocationToken,
|
||||
Registry.get(getTldFromDomainName(domainName)).getStandardRenewCost(dateTime));
|
||||
Tld.get(getTldFromDomainName(domainName)).getStandardRenewCost(dateTime));
|
||||
isRenewCostPremiumPrice = false;
|
||||
break;
|
||||
default:
|
||||
@@ -168,7 +168,7 @@ public final class DomainPricingLogic {
|
||||
.addFeeOrCredit(
|
||||
Fee.create(renewCost.getAmount(), FeeType.RENEW, isRenewCostPremiumPrice))
|
||||
.build())
|
||||
.setRegistry(registry)
|
||||
.setTld(tld)
|
||||
.setDomainName(InternetDomainName.from(domainName))
|
||||
.setAsOfDate(dateTime)
|
||||
.setYears(years)
|
||||
@@ -176,15 +176,14 @@ public final class DomainPricingLogic {
|
||||
}
|
||||
|
||||
/** Returns a new restore price for the pricer. */
|
||||
FeesAndCredits getRestorePrice(
|
||||
Registry registry, String domainName, DateTime dateTime, boolean isExpired)
|
||||
FeesAndCredits getRestorePrice(Tld tld, String domainName, DateTime dateTime, boolean isExpired)
|
||||
throws EppException {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
FeesAndCredits.Builder feesAndCredits =
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(registry.getCurrency())
|
||||
.setCurrency(tld.getCurrency())
|
||||
.addFeeOrCredit(
|
||||
Fee.create(registry.getStandardRestoreCost().getAmount(), FeeType.RESTORE, false));
|
||||
Fee.create(tld.getStandardRestoreCost().getAmount(), FeeType.RESTORE, false));
|
||||
if (isExpired) {
|
||||
feesAndCredits.addFeeOrCredit(
|
||||
Fee.create(
|
||||
@@ -193,7 +192,7 @@ public final class DomainPricingLogic {
|
||||
return customLogic.customizeRestorePrice(
|
||||
RestorePriceParameters.newBuilder()
|
||||
.setFeesAndCredits(feesAndCredits.build())
|
||||
.setRegistry(registry)
|
||||
.setTld(tld)
|
||||
.setDomainName(InternetDomainName.from(domainName))
|
||||
.setAsOfDate(dateTime)
|
||||
.build());
|
||||
@@ -201,34 +200,30 @@ public final class DomainPricingLogic {
|
||||
|
||||
/** Returns a new transfer price for the pricer. */
|
||||
FeesAndCredits getTransferPrice(
|
||||
Registry registry,
|
||||
String domainName,
|
||||
DateTime dateTime,
|
||||
@Nullable Recurring recurringBillingEvent)
|
||||
Tld tld, String domainName, DateTime dateTime, @Nullable Recurring recurringBillingEvent)
|
||||
throws EppException {
|
||||
FeesAndCredits renewPrice =
|
||||
getRenewPrice(registry, domainName, dateTime, 1, recurringBillingEvent, Optional.empty());
|
||||
getRenewPrice(tld, domainName, dateTime, 1, recurringBillingEvent, Optional.empty());
|
||||
return customLogic.customizeTransferPrice(
|
||||
TransferPriceParameters.newBuilder()
|
||||
.setFeesAndCredits(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(registry.getCurrency())
|
||||
.setCurrency(tld.getCurrency())
|
||||
.addFeeOrCredit(
|
||||
Fee.create(
|
||||
renewPrice.getRenewCost().getAmount(),
|
||||
FeeType.RENEW,
|
||||
renewPrice.hasAnyPremiumFees()))
|
||||
.build())
|
||||
.setRegistry(registry)
|
||||
.setTld(tld)
|
||||
.setDomainName(InternetDomainName.from(domainName))
|
||||
.setAsOfDate(dateTime)
|
||||
.build());
|
||||
}
|
||||
|
||||
/** Returns a new update price for the pricer. */
|
||||
FeesAndCredits getUpdatePrice(Registry registry, String domainName, DateTime dateTime)
|
||||
throws EppException {
|
||||
CurrencyUnit currency = registry.getCurrency();
|
||||
FeesAndCredits getUpdatePrice(Tld tld, String domainName, DateTime dateTime) throws EppException {
|
||||
CurrencyUnit currency = tld.getCurrency();
|
||||
BaseFee feeOrCredit = Fee.create(zeroInCurrency(currency), FeeType.UPDATE, false);
|
||||
return customLogic.customizeUpdatePrice(
|
||||
UpdatePriceParameters.newBuilder()
|
||||
@@ -237,7 +232,7 @@ public final class DomainPricingLogic {
|
||||
.setCurrency(currency)
|
||||
.setFeesAndCredits(feeOrCredit)
|
||||
.build())
|
||||
.setRegistry(registry)
|
||||
.setTld(tld)
|
||||
.setDomainName(InternetDomainName.from(domainName))
|
||||
.setAsOfDate(dateTime)
|
||||
.build());
|
||||
|
||||
@@ -66,6 +66,7 @@ import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.BaseFee.FeeType;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.fee.FeeRenewCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
@@ -83,7 +84,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.money.Money;
|
||||
@@ -172,14 +173,25 @@ 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
|
||||
String tldStr = existingDomain.getTld();
|
||||
Tld tld = Tld.get(tldStr);
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Registry.get(existingDomain.getTld()),
|
||||
tld,
|
||||
registrarId,
|
||||
now,
|
||||
CommandName.RENEW,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
boolean defaultTokenUsed = false;
|
||||
if (!allocationToken.isPresent()) {
|
||||
allocationToken =
|
||||
DomainFlowUtils.checkForDefaultToken(
|
||||
tld, existingDomain.getDomainName(), CommandName.RENEW, registrarId, now);
|
||||
if (allocationToken.isPresent()) {
|
||||
defaultTokenUsed = true;
|
||||
}
|
||||
}
|
||||
verifyRenewAllowed(authInfo, existingDomain, command, allocationToken);
|
||||
|
||||
// If client passed an applicable static token this updates the domain
|
||||
@@ -195,13 +207,13 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
tm().loadByKey(existingDomain.getAutorenewBillingEvent());
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getRenewPrice(
|
||||
Registry.get(existingDomain.getTld()),
|
||||
Tld.get(existingDomain.getTld()),
|
||||
targetId,
|
||||
now,
|
||||
years,
|
||||
existingRecurringBillingEvent,
|
||||
allocationToken);
|
||||
validateFeeChallenge(feeRenew, feesAndCredits, false);
|
||||
validateFeeChallenge(feeRenew, feesAndCredits, defaultTokenUsed);
|
||||
flowCustomLogic.afterValidation(
|
||||
AfterValidationParameters.newBuilder()
|
||||
.setExistingDomain(existingDomain)
|
||||
@@ -210,11 +222,10 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
.build());
|
||||
HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
|
||||
historyBuilder.setRevisionId(domainHistoryId.getRevisionId());
|
||||
String tld = existingDomain.getTld();
|
||||
// Bill for this explicit renew itself.
|
||||
BillingEvent.OneTime explicitRenewEvent =
|
||||
createRenewBillingEvent(
|
||||
tld, feesAndCredits.getTotalCost(), years, domainHistoryId, allocationToken, now);
|
||||
tldStr, feesAndCredits.getTotalCost(), years, domainHistoryId, allocationToken, now);
|
||||
// Create a new autorenew billing event and poll message starting at the new expiration time.
|
||||
BillingEvent.Recurring newAutorenewEvent =
|
||||
newAutorenewBillingEvent(existingDomain)
|
||||
@@ -243,10 +254,8 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
GracePeriod.forBillingEvent(
|
||||
GracePeriodStatus.RENEW, existingDomain.getRepoId(), explicitRenewEvent))
|
||||
.build();
|
||||
Registry registry = Registry.get(existingDomain.getTld());
|
||||
DomainHistory domainHistory =
|
||||
buildDomainHistory(
|
||||
newDomain, now, command.getPeriod(), registry.getRenewGracePeriodLength());
|
||||
buildDomainHistory(newDomain, now, command.getPeriod(), tld.getRenewGracePeriodLength());
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(
|
||||
newDomain, domainHistory, explicitRenewEvent, newAutorenewEvent, newAutorenewPollMessage);
|
||||
@@ -348,7 +357,7 @@ public final class DomainRenewFlow implements TransactionalFlow {
|
||||
.filter(t -> AllocationToken.TokenBehavior.DEFAULT.equals(t.getTokenBehavior()))
|
||||
.map(AllocationToken::createVKey)
|
||||
.orElse(null))
|
||||
.setBillingTime(now.plus(Registry.get(tld).getRenewGracePeriodLength()))
|
||||
.setBillingTime(now.plus(Tld.get(tld).getRenewGracePeriodLength()))
|
||||
.setDomainHistoryId(domainHistoryId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.money.Money;
|
||||
@@ -141,8 +141,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
boolean isExpired = existingDomain.getRegistrationExpirationTime().isBefore(now);
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getRestorePrice(
|
||||
Registry.get(existingDomain.getTld()), targetId, now, isExpired);
|
||||
pricingLogic.getRestorePrice(Tld.get(existingDomain.getTld()), targetId, now, isExpired);
|
||||
Optional<FeeUpdateCommandExtension> feeUpdate =
|
||||
eppInput.getSingleExtension(FeeUpdateCommandExtension.class);
|
||||
verifyRestoreAllowed(command, existingDomain, feeUpdate, feesAndCredits, now);
|
||||
|
||||
@@ -53,6 +53,7 @@ import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
@@ -63,7 +64,7 @@ import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import java.util.Optional;
|
||||
@@ -132,16 +133,17 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Registry.get(existingDomain.getTld()),
|
||||
Tld.get(existingDomain.getTld()),
|
||||
registrarId,
|
||||
now,
|
||||
CommandName.TRANSFER,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||
verifyHasPendingTransfer(existingDomain);
|
||||
verifyResourceOwnership(registrarId, existingDomain);
|
||||
String tld = existingDomain.getTld();
|
||||
String tldStr = existingDomain.getTld();
|
||||
if (!isSuperuser) {
|
||||
checkAllowedAccessToTld(registrarId, tld);
|
||||
checkAllowedAccessToTld(registrarId, tldStr);
|
||||
}
|
||||
DomainTransferData transferData = existingDomain.getTransferData();
|
||||
String gainingRegistrarId = transferData.getGainingRegistrarId();
|
||||
@@ -164,7 +166,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
.setCost(
|
||||
pricingLogic
|
||||
.getTransferPrice(
|
||||
Registry.get(tld),
|
||||
Tld.get(tldStr),
|
||||
targetId,
|
||||
transferData.getTransferRequestTime(),
|
||||
// When removing a domain from a package it should return to the
|
||||
@@ -173,7 +175,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
hasPackageToken ? null : existingRecurring)
|
||||
.getRenewCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now.plus(Registry.get(tld).getTransferGracePeriodLength()))
|
||||
.setBillingTime(now.plus(Tld.get(tldStr).getTransferGracePeriodLength()))
|
||||
.setDomainHistoryId(domainHistoryId)
|
||||
.build());
|
||||
|
||||
@@ -260,8 +262,8 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
.setCurrentPackageToken(null)
|
||||
.build();
|
||||
|
||||
Registry registry = Registry.get(existingDomain.getTld());
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, registry, now, gainingRegistrarId);
|
||||
Tld tld = Tld.get(existingDomain.getTld());
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, tld, now, gainingRegistrarId);
|
||||
// Create a poll message for the gaining client.
|
||||
PollMessage gainingClientPollMessage =
|
||||
createGainingTransferPollMessage(
|
||||
@@ -284,12 +286,12 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
}
|
||||
|
||||
private DomainHistory buildDomainHistory(
|
||||
Domain newDomain, Registry registry, DateTime now, String gainingRegistrarId) {
|
||||
Domain newDomain, Tld tld, DateTime now, String gainingRegistrarId) {
|
||||
ImmutableSet<DomainTransactionRecord> cancelingRecords =
|
||||
createCancelingRecords(
|
||||
newDomain,
|
||||
now,
|
||||
registry.getAutomaticTransferLength().plus(registry.getTransferGracePeriodLength()),
|
||||
tld.getAutomaticTransferLength().plus(tld.getTransferGracePeriodLength()),
|
||||
ImmutableSet.of(TRANSFER_SUCCESSFUL));
|
||||
return historyBuilder
|
||||
.setType(DOMAIN_TRANSFER_APPROVE)
|
||||
@@ -300,7 +302,7 @@ public final class DomainTransferApproveFlow implements TransactionalFlow {
|
||||
cancelingRecords,
|
||||
DomainTransactionRecord.create(
|
||||
newDomain.getTld(),
|
||||
now.plus(registry.getTransferGracePeriodLength()),
|
||||
now.plus(tld.getTransferGracePeriodLength()),
|
||||
TRANSFER_SUCCESSFUL,
|
||||
1)))
|
||||
.build();
|
||||
|
||||
@@ -48,7 +48,7 @@ import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -100,7 +100,7 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
|
||||
if (!isSuperuser) {
|
||||
checkAllowedAccessToTld(registrarId, existingDomain.getTld());
|
||||
}
|
||||
Registry registry = Registry.get(existingDomain.getTld());
|
||||
Tld tld = Tld.get(existingDomain.getTld());
|
||||
|
||||
HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
|
||||
historyBuilder
|
||||
@@ -109,7 +109,7 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
|
||||
|
||||
Domain newDomain =
|
||||
denyPendingTransfer(existingDomain, TransferStatus.CLIENT_CANCELLED, now, registrarId);
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, registry, now);
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, tld, now);
|
||||
tm().putAll(
|
||||
newDomain,
|
||||
domainHistory,
|
||||
@@ -128,12 +128,12 @@ public final class DomainTransferCancelFlow implements TransactionalFlow {
|
||||
.build();
|
||||
}
|
||||
|
||||
private DomainHistory buildDomainHistory(Domain newDomain, Registry registry, DateTime now) {
|
||||
private DomainHistory buildDomainHistory(Domain newDomain, Tld tld, DateTime now) {
|
||||
ImmutableSet<DomainTransactionRecord> cancelingRecords =
|
||||
createCancelingRecords(
|
||||
newDomain,
|
||||
now,
|
||||
registry.getAutomaticTransferLength().plus(registry.getTransferGracePeriodLength()),
|
||||
tld.getAutomaticTransferLength().plus(tld.getTransferGracePeriodLength()),
|
||||
ImmutableSet.of(TRANSFER_SUCCESSFUL));
|
||||
return historyBuilder
|
||||
.setType(DOMAIN_TRANSFER_CANCEL)
|
||||
|
||||
@@ -50,7 +50,7 @@ import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -94,7 +94,7 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
|
||||
extensionManager.validate();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
Registry registry = Registry.get(existingDomain.getTld());
|
||||
Tld tld = Tld.get(existingDomain.getTld());
|
||||
HistoryEntryId domainHistoryId = createHistoryEntryId(existingDomain);
|
||||
historyBuilder
|
||||
.setRevisionId(domainHistoryId.getRevisionId())
|
||||
@@ -108,7 +108,7 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
|
||||
}
|
||||
Domain newDomain =
|
||||
denyPendingTransfer(existingDomain, TransferStatus.CLIENT_REJECTED, now, registrarId);
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, registry, now);
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, tld, now);
|
||||
tm().putAll(
|
||||
newDomain,
|
||||
domainHistory,
|
||||
@@ -127,12 +127,12 @@ public final class DomainTransferRejectFlow implements TransactionalFlow {
|
||||
.build();
|
||||
}
|
||||
|
||||
private DomainHistory buildDomainHistory(Domain newDomain, Registry registry, DateTime now) {
|
||||
private DomainHistory buildDomainHistory(Domain newDomain, Tld tld, DateTime now) {
|
||||
ImmutableSet<DomainTransactionRecord> cancelingRecords =
|
||||
createCancelingRecords(
|
||||
newDomain,
|
||||
now,
|
||||
registry.getAutomaticTransferLength().plus(registry.getTransferGracePeriodLength()),
|
||||
tld.getAutomaticTransferLength().plus(tld.getTransferGracePeriodLength()),
|
||||
ImmutableSet.of(TRANSFER_SUCCESSFUL));
|
||||
return historyBuilder
|
||||
.setType(DOMAIN_TRANSFER_REJECT)
|
||||
|
||||
@@ -57,6 +57,7 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Transfer;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.fee.FeeTransferCommandExtension;
|
||||
import google.registry.model.domain.fee.FeeTransformResponseExtension;
|
||||
import google.registry.model.domain.metadata.MetadataExtension;
|
||||
@@ -73,7 +74,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
|
||||
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||
import google.registry.model.transfer.TransferResponse.DomainTransferResponse;
|
||||
@@ -170,9 +171,10 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
Domain existingDomain = loadAndVerifyExistence(Domain.class, targetId, now);
|
||||
allocationTokenFlowUtils.verifyAllocationTokenIfPresent(
|
||||
existingDomain,
|
||||
Registry.get(existingDomain.getTld()),
|
||||
Tld.get(existingDomain.getTld()),
|
||||
gainingClientId,
|
||||
now,
|
||||
CommandName.TRANSFER,
|
||||
eppInput.getSingleExtension(AllocationTokenExtension.class));
|
||||
Optional<DomainTransferRequestSuperuserExtension> superuserExtension =
|
||||
eppInput.getSingleExtension(DomainTransferRequestSuperuserExtension.class);
|
||||
@@ -182,8 +184,8 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
: ((Transfer) resourceCommand).getPeriod();
|
||||
verifyTransferAllowed(existingDomain, period, now, superuserExtension);
|
||||
|
||||
String tld = existingDomain.getTld();
|
||||
Registry registry = Registry.get(tld);
|
||||
String tldStr = existingDomain.getTld();
|
||||
Tld tld = Tld.get(tldStr);
|
||||
// An optional extension from the client specifying what they think the transfer should cost.
|
||||
Optional<FeeTransferCommandExtension> feeTransfer =
|
||||
eppInput.getSingleExtension(FeeTransferCommandExtension.class);
|
||||
@@ -199,14 +201,14 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
feesAndCredits = Optional.empty();
|
||||
} else if (!existingDomain.getCurrentPackageToken().isPresent()) {
|
||||
feesAndCredits =
|
||||
Optional.of(pricingLogic.getTransferPrice(registry, targetId, now, existingRecurring));
|
||||
Optional.of(pricingLogic.getTransferPrice(tld, targetId, now, existingRecurring));
|
||||
} else {
|
||||
// If existing domain is in a package, calculate the transfer price with default renewal price
|
||||
// behavior
|
||||
feesAndCredits =
|
||||
period.getValue() == 0
|
||||
? Optional.empty()
|
||||
: Optional.of(pricingLogic.getTransferPrice(registry, targetId, now, null));
|
||||
: Optional.of(pricingLogic.getTransferPrice(tld, targetId, now, null));
|
||||
}
|
||||
|
||||
if (feesAndCredits.isPresent()) {
|
||||
@@ -222,7 +224,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
domainTransferRequestSuperuserExtension ->
|
||||
now.plusDays(
|
||||
domainTransferRequestSuperuserExtension.getAutomaticTransferLength()))
|
||||
.orElseGet(() -> now.plus(registry.getAutomaticTransferLength()));
|
||||
.orElseGet(() -> now.plus(tld.getAutomaticTransferLength()));
|
||||
// If the domain will be in the auto-renew grace period at the moment of transfer, the transfer
|
||||
// will subsume the autorenew, so we don't add the normal extra year from the transfer.
|
||||
// The gaining registrar is still billed for the extra year; the losing registrar will get a
|
||||
@@ -281,7 +283,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
.setLastEppUpdateTime(now)
|
||||
.setLastEppUpdateRegistrarId(gainingClientId)
|
||||
.build();
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, registry, now, period);
|
||||
DomainHistory domainHistory = buildDomainHistory(newDomain, tld, now, period);
|
||||
|
||||
asyncTaskEnqueuer.enqueueAsyncResave(newDomain.createVKey(), now, automaticTransferTime);
|
||||
tm().putAll(
|
||||
@@ -361,8 +363,7 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
}
|
||||
}
|
||||
|
||||
private DomainHistory buildDomainHistory(
|
||||
Domain newDomain, Registry registry, DateTime now, Period period) {
|
||||
private DomainHistory buildDomainHistory(Domain newDomain, Tld tld, DateTime now, Period period) {
|
||||
return historyBuilder
|
||||
.setType(DOMAIN_TRANSFER_REQUEST)
|
||||
.setPeriod(period)
|
||||
@@ -370,9 +371,9 @@ public final class DomainTransferRequestFlow implements TransactionalFlow {
|
||||
.setDomainTransactionRecords(
|
||||
ImmutableSet.of(
|
||||
DomainTransactionRecord.create(
|
||||
registry.getTldStr(),
|
||||
now.plus(registry.getAutomaticTransferLength())
|
||||
.plus(registry.getTransferGracePeriodLength()),
|
||||
tld.getTldStr(),
|
||||
now.plus(tld.getAutomaticTransferLength())
|
||||
.plus(tld.getTransferGracePeriodLength()),
|
||||
TransactionReportField.TRANSFER_SUCCESSFUL,
|
||||
1)))
|
||||
.build();
|
||||
|
||||
@@ -33,7 +33,7 @@ import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
|
||||
@@ -127,7 +127,7 @@ public final class DomainTransferUtils {
|
||||
.setTransferredRegistrationExpirationTime(serverApproveNewExpirationTime)
|
||||
.setTransferStatus(TransferStatus.SERVER_APPROVED)
|
||||
.build();
|
||||
Registry registry = Registry.get(existingDomain.getTld());
|
||||
Tld tld = Tld.get(existingDomain.getTld());
|
||||
ImmutableSet.Builder<TransferServerApproveEntity> builder = new ImmutableSet.Builder<>();
|
||||
transferCost.ifPresent(
|
||||
cost ->
|
||||
@@ -137,7 +137,7 @@ public final class DomainTransferUtils {
|
||||
domainHistoryId,
|
||||
targetId,
|
||||
gainingRegistrarId,
|
||||
registry,
|
||||
tld,
|
||||
cost)));
|
||||
createOptionalAutorenewCancellation(
|
||||
automaticTransferTime, now, domainHistoryId, targetId, existingDomain, transferCost)
|
||||
@@ -308,7 +308,7 @@ public final class DomainTransferUtils {
|
||||
HistoryEntryId domainHistoryId,
|
||||
String targetId,
|
||||
String gainingRegistrarId,
|
||||
Registry registry,
|
||||
Tld registry,
|
||||
Money transferCost) {
|
||||
return new BillingEvent.OneTime.Builder()
|
||||
.setReason(Reason.TRANSFER)
|
||||
|
||||
@@ -86,7 +86,7 @@ import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -219,18 +219,18 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||
AddRemove add = command.getInnerAdd();
|
||||
AddRemove remove = command.getInnerRemove();
|
||||
String tld = existingDomain.getTld();
|
||||
String tldStr = existingDomain.getTld();
|
||||
if (!isSuperuser) {
|
||||
verifyNoDisallowedStatuses(existingDomain, UPDATE_DISALLOWED_STATUSES);
|
||||
verifyResourceOwnership(registrarId, existingDomain);
|
||||
verifyClientUpdateNotProhibited(command, existingDomain);
|
||||
verifyAllStatusesAreClientSettable(union(add.getStatusValues(), remove.getStatusValues()));
|
||||
checkAllowedAccessToTld(registrarId, tld);
|
||||
checkAllowedAccessToTld(registrarId, tldStr);
|
||||
}
|
||||
Registry registry = Registry.get(tld);
|
||||
Tld tld = Tld.get(tldStr);
|
||||
Optional<FeeUpdateCommandExtension> feeUpdate =
|
||||
eppInput.getSingleExtension(FeeUpdateCommandExtension.class);
|
||||
FeesAndCredits feesAndCredits = pricingLogic.getUpdatePrice(registry, targetId, now);
|
||||
FeesAndCredits feesAndCredits = pricingLogic.getUpdatePrice(tld, targetId, now);
|
||||
validateFeesAckedIfPresent(feeUpdate, feesAndCredits, false);
|
||||
verifyNotInPendingDelete(
|
||||
add.getContacts(),
|
||||
@@ -238,8 +238,8 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
add.getNameservers());
|
||||
validateContactsHaveTypes(add.getContacts());
|
||||
validateContactsHaveTypes(remove.getContacts());
|
||||
validateRegistrantAllowedOnTld(tld, command.getInnerChange().getRegistrantContactId());
|
||||
validateNameserversAllowedOnTld(tld, add.getNameserverHostNames());
|
||||
validateRegistrantAllowedOnTld(tldStr, command.getInnerChange().getRegistrantContactId());
|
||||
validateNameserversAllowedOnTld(tldStr, add.getNameserverHostNames());
|
||||
}
|
||||
|
||||
private Domain performUpdate(Update command, Domain domain, DateTime now) throws EppException {
|
||||
@@ -338,7 +338,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
.setReason(Reason.SERVER_STATUS)
|
||||
.setTargetId(targetId)
|
||||
.setRegistrarId(registrarId)
|
||||
.setCost(Registry.get(existingDomain.getTld()).getServerStatusChangeCost())
|
||||
.setCost(Tld.get(existingDomain.getTld()).getServerStatusChangeCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now)
|
||||
.setDomainHistory(historyEntry)
|
||||
|
||||
@@ -22,7 +22,7 @@ import google.registry.flows.EppException;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
@@ -36,7 +36,7 @@ public class AllocationTokenCustomLogic {
|
||||
public AllocationToken validateToken(
|
||||
DomainCommand.Create command,
|
||||
AllocationToken token,
|
||||
Registry registry,
|
||||
Tld tld,
|
||||
String registrarId,
|
||||
DateTime now)
|
||||
throws EppException {
|
||||
@@ -46,7 +46,7 @@ public class AllocationTokenCustomLogic {
|
||||
|
||||
/** Performs additional custom logic for validating a token on an existing domain. */
|
||||
public AllocationToken validateToken(
|
||||
Domain domain, AllocationToken token, Registry registry, String registrarId, DateTime now)
|
||||
Domain domain, AllocationToken token, Tld tld, String registrarId, DateTime now)
|
||||
throws EppException {
|
||||
// Do nothing.
|
||||
return token;
|
||||
|
||||
@@ -30,13 +30,14 @@ import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand;
|
||||
import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.domain.token.AllocationTokenExtension;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -78,7 +79,7 @@ public class AllocationTokenFlowUtils {
|
||||
ImmutableMap.Builder<InternetDomainName, String> resultsBuilder = new ImmutableMap.Builder<>();
|
||||
for (InternetDomainName domainName : domainNames) {
|
||||
try {
|
||||
validateToken(domainName, tokenEntity, registrarId, now);
|
||||
validateToken(domainName, tokenEntity, CommandName.CREATE, registrarId, now);
|
||||
validDomainNames.add(domainName);
|
||||
} catch (EppException e) {
|
||||
resultsBuilder.put(domainName, e.getMessage());
|
||||
@@ -109,11 +110,19 @@ public class AllocationTokenFlowUtils {
|
||||
* @throws EppException if the token is invalid in any way
|
||||
*/
|
||||
public static void validateToken(
|
||||
InternetDomainName domainName, AllocationToken token, String registrarId, DateTime now)
|
||||
InternetDomainName domainName,
|
||||
AllocationToken token,
|
||||
CommandName commandName,
|
||||
String registrarId,
|
||||
DateTime now)
|
||||
throws EppException {
|
||||
|
||||
// Only tokens with default behavior require validation
|
||||
if (TokenBehavior.DEFAULT.equals(token.getTokenBehavior())) {
|
||||
if (!token.getAllowedEppActions().isEmpty()
|
||||
&& !token.getAllowedEppActions().contains(commandName)) {
|
||||
throw new AllocationTokenNotValidForCommandException();
|
||||
}
|
||||
if (!token.getAllowedRegistrarIds().isEmpty()
|
||||
&& !token.getAllowedRegistrarIds().contains(registrarId)) {
|
||||
throw new AllocationTokenNotValidForRegistrarException();
|
||||
@@ -164,24 +173,7 @@ public class AllocationTokenFlowUtils {
|
||||
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
||||
public Optional<AllocationToken> verifyAllocationTokenCreateIfPresent(
|
||||
DomainCommand.Create command,
|
||||
Registry registry,
|
||||
String registrarId,
|
||||
DateTime now,
|
||||
Optional<AllocationTokenExtension> extension)
|
||||
throws EppException {
|
||||
if (!extension.isPresent()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
validateToken(InternetDomainName.from(command.getDomainName()), tokenEntity, registrarId, now);
|
||||
return Optional.of(
|
||||
tokenCustomLogic.validateToken(command, tokenEntity, registry, registrarId, now));
|
||||
}
|
||||
|
||||
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
||||
public Optional<AllocationToken> verifyAllocationTokenIfPresent(
|
||||
Domain existingDomain,
|
||||
Registry registry,
|
||||
Tld tld,
|
||||
String registrarId,
|
||||
DateTime now,
|
||||
Optional<AllocationTokenExtension> extension)
|
||||
@@ -191,9 +183,35 @@ public class AllocationTokenFlowUtils {
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
validateToken(
|
||||
InternetDomainName.from(existingDomain.getDomainName()), tokenEntity, registrarId, now);
|
||||
InternetDomainName.from(command.getDomainName()),
|
||||
tokenEntity,
|
||||
CommandName.CREATE,
|
||||
registrarId,
|
||||
now);
|
||||
return Optional.of(tokenCustomLogic.validateToken(command, tokenEntity, tld, registrarId, now));
|
||||
}
|
||||
|
||||
/** Verifies and returns the allocation token if one is specified, otherwise does nothing. */
|
||||
public Optional<AllocationToken> verifyAllocationTokenIfPresent(
|
||||
Domain existingDomain,
|
||||
Tld tld,
|
||||
String registrarId,
|
||||
DateTime now,
|
||||
CommandName commandName,
|
||||
Optional<AllocationTokenExtension> extension)
|
||||
throws EppException {
|
||||
if (!extension.isPresent()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
AllocationToken tokenEntity = loadToken(extension.get().getAllocationToken());
|
||||
validateToken(
|
||||
InternetDomainName.from(existingDomain.getDomainName()),
|
||||
tokenEntity,
|
||||
commandName,
|
||||
registrarId,
|
||||
now);
|
||||
return Optional.of(
|
||||
tokenCustomLogic.validateToken(existingDomain, tokenEntity, registry, registrarId, now));
|
||||
tokenCustomLogic.validateToken(existingDomain, tokenEntity, tld, registrarId, now));
|
||||
}
|
||||
|
||||
public static void verifyTokenAllowedOnDomain(
|
||||
@@ -280,6 +298,14 @@ public class AllocationTokenFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is not valid for this EPP command. */
|
||||
public static class AllocationTokenNotValidForCommandException
|
||||
extends AssociationProhibitsOperationException {
|
||||
AllocationTokenNotValidForCommandException() {
|
||||
super("Allocation token not valid for the EPP command");
|
||||
}
|
||||
}
|
||||
|
||||
/** The allocation token is invalid. */
|
||||
public static class InvalidAllocationTokenException extends AuthorizationErrorException {
|
||||
InvalidAllocationTokenException() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,23 @@
|
||||
# Registry: Charleston Road Registry Inc.
|
||||
#
|
||||
# Script: Latn
|
||||
#
|
||||
# Version: 1.0
|
||||
#
|
||||
# Effective Date: 04-12-2012
|
||||
#
|
||||
# Address: 1600 Amphitheatre Parkway Mountain View, CA 94043, USA
|
||||
#
|
||||
# Version: 2.0
|
||||
# Effective Date: 2023-04-04
|
||||
# URL: https://www.iana.org/domains/idn-tables/tables/google_latn_2.0.txt
|
||||
# Policy: https://www.registry.google/about/policies/domainabuse/
|
||||
# Contact Name: CRR Tech
|
||||
# Email address: crr-tech@google.com
|
||||
# Telephone: +1 (650) 253-0000
|
||||
#
|
||||
# Website: www.charlestonroadregistry.com
|
||||
# Code points requiring context rules
|
||||
#
|
||||
# Code point Description of rule/Reference
|
||||
#
|
||||
# U+002D Label must neither start nor end with U+002D. Label
|
||||
# HYPHEN-MINUS must not have U+002D in both third and fourth
|
||||
# position. RFC 5891 (sec 4.2.3.1)
|
||||
#
|
||||
# Notes: This table describes codepoints allowed for the Latin script.
|
||||
|
||||
U+002D # HYPHEN-MINUS
|
||||
#
|
||||
U+0030 # DIGIT ZERO
|
||||
U+0031 # DIGIT ONE
|
||||
U+0032 # DIGIT TWO
|
||||
@@ -26,12 +28,7 @@ U+0036 # DIGIT SIX
|
||||
U+0037 # DIGIT SEVEN
|
||||
U+0038 # DIGIT EIGHT
|
||||
U+0039 # DIGIT NINE
|
||||
#
|
||||
# The following code points are listed according to the
|
||||
# European Ordering Rules (ENV 13710).
|
||||
#
|
||||
U+0061 # LATIN SMALL LETTER A
|
||||
U+00E1 # LATIN SMALL LETTER A WITH ACUTE
|
||||
U+00E0 # LATIN SMALL LETTER A WITH GRAVE
|
||||
U+0103 # LATIN SMALL LETTER A WITH BREVE
|
||||
U+00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX
|
||||
@@ -39,14 +36,11 @@ U+00E5 # LATIN SMALL LETTER A WITH RING ABOVE
|
||||
U+00E4 # LATIN SMALL LETTER A WITH DIAERESIS
|
||||
U+00E3 # LATIN SMALL LETTER A WITH TILDE
|
||||
U+0105 # LATIN SMALL LETTER A WITH OGONEK
|
||||
U+0101 # LATIN SMALL LETTER A WITH MACRON
|
||||
U+01CE # LATIN SMALL LETTER A WITH CARON
|
||||
U+00E6 # LATIN SMALL LETTER AE
|
||||
U+0062 # LATIN SMALL LETTER B
|
||||
U+0063 # LATIN SMALL LETTER C
|
||||
U+0107 # LATIN SMALL LETTER C WITH ACUTE
|
||||
U+010D # LATIN SMALL LETTER C WITH CARON
|
||||
U+010B # LATIN SMALL LETTER C WITH DOT ABOVE
|
||||
U+00E7 # LATIN SMALL LETTER C WITH CEDILLA
|
||||
U+0064 # LATIN SMALL LETTER D
|
||||
U+010F # LATIN SMALL LETTER D WITH CARON
|
||||
@@ -65,20 +59,14 @@ U+0259 # LATIN SMALL LETTER SCHWA
|
||||
U+0066 # LATIN SMALL LETTER F
|
||||
U+0067 # LATIN SMALL LETTER G
|
||||
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+0068 # LATIN SMALL LETTER H
|
||||
U+0127 # LATIN SMALL LETTER H WITH STROKE
|
||||
U+0069 # LATIN SMALL LETTER I
|
||||
U+0131 # LATIN SMALL LETTER DOTLESS I
|
||||
U+00ED # LATIN SMALL LETTER I WITH ACUTE
|
||||
U+00EC # LATIN SMALL LETTER I WITH GRAVE
|
||||
U+00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX
|
||||
U+00EF # LATIN SMALL LETTER I WITH DIAERESIS
|
||||
U+012F # LATIN SMALL LETTER I WITH OGONEK
|
||||
U+012B # LATIN SMALL LETTER I WITH MACRON
|
||||
U+01D0 # LATIN SMALL LETTER I WITH CARON
|
||||
U+006A # LATIN SMALL LETTER J
|
||||
U+006B # LATIN SMALL LETTER K
|
||||
U+01E9 # LATIN SMALL LETTER K WITH CARON
|
||||
@@ -90,20 +78,15 @@ U+013C # LATIN SMALL LETTER L WITH CEDILLA
|
||||
U+0142 # LATIN SMALL LETTER L WITH STROKE
|
||||
U+006D # LATIN SMALL LETTER M
|
||||
U+006E # LATIN SMALL LETTER N
|
||||
U+0144 # LATIN SMALL LETTER N WITH ACUTE
|
||||
U+0148 # LATIN SMALL LETTER N WITH CARON
|
||||
U+00F1 # LATIN SMALL LETTER N WITH TILDE
|
||||
U+0146 # LATIN SMALL LETTER N WITH CEDILLA
|
||||
U+014B # LATIN SMALL LETTER ENG
|
||||
U+006F # LATIN SMALL LETTER O
|
||||
U+00F3 # LATIN SMALL LETTER O WITH ACUTE
|
||||
U+00F2 # LATIN SMALL LETTER O WITH GRAVE
|
||||
U+00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX
|
||||
U+00F6 # LATIN SMALL LETTER O WITH DIAERESIS
|
||||
U+0151 # LATIN SMALL LETTER O WITH DOUBLE ACUTE
|
||||
U+00F5 # LATIN SMALL LETTER O WITH TILDE
|
||||
U+014D # LATIN SMALL LETTER O WITH MACRON
|
||||
U+01D2 # LATIN SMALL LETTER O WITH CARON
|
||||
U+00F8 # LATIN SMALL LETTER O WITH STROKE
|
||||
U+0153 # LATIN SMALL LIGATURE OE
|
||||
U+0070 # LATIN SMALL LETTER P
|
||||
@@ -111,41 +94,31 @@ U+0071 # LATIN SMALL LETTER Q
|
||||
U+0072 # LATIN SMALL LETTER R
|
||||
U+0155 # LATIN SMALL LETTER R WITH ACUTE
|
||||
U+0159 # LATIN SMALL LETTER R WITH CARON
|
||||
U+0157 # LATIN SMALL LETTER R WITH CEDILLA
|
||||
U+0073 # LATIN SMALL LETTER S
|
||||
U+015B # LATIN SMALL LETTER S WITH ACUTE
|
||||
U+0161 # LATIN SMALL LETTER S WITH CARON
|
||||
U+015F # LATIN SMALL LETTER S WITH CEDILLA
|
||||
U+0074 # LATIN SMALL LETTER T
|
||||
U+0165 # LATIN SMALL LETTER T WITH CARON
|
||||
U+0163 # LATIN SMALL LETTER T WITH CEDILLA
|
||||
U+0167 # LATIN SMALL LETTER T WITH STROKE
|
||||
U+0075 # LATIN SMALL LETTER U
|
||||
U+00FA # LATIN SMALL LETTER U WITH ACUTE
|
||||
U+00F9 # LATIN SMALL LETTER U WITH GRAVE
|
||||
U+00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX
|
||||
U+016F # LATIN SMALL LETTER U WITH RING ABOVE
|
||||
U+00FC # LATIN SMALL LETTER U WITH DIAERESIS
|
||||
U+0171 # LATIN SMALL LETTER U WITH DOUBLE ACUTE
|
||||
U+0173 # LATIN SMALL LETTER U WITH OGONEK
|
||||
U+016B # LATIN SMALL LETTER U WITH MACRON
|
||||
U+01D4 # LATIN SMALL LETTER U WITH CARON
|
||||
U+0076 # LATIN SMALL LETTER V
|
||||
U+0077 # LATIN SMALL LETTER W
|
||||
U+1E83 # LATIN SMALL LETTER W WITH ACUTE
|
||||
U+1E81 # LATIN SMALL LETTER W WITH GRAVE
|
||||
U+0175 # LATIN SMALL LETTER W WITH CIRCUMFLEX
|
||||
U+1E85 # LATIN SMALL LETTER W WITH DIAERESIS
|
||||
U+0078 # LATIN SMALL LETTER X
|
||||
U+0079 # LATIN SMALL LETTER Y
|
||||
U+00FD # LATIN SMALL LETTER Y WITH ACUTE
|
||||
U+1EF3 # LATIN SMALL LETTER Y WITH GRAVE
|
||||
U+0177 # LATIN SMALL LETTER Y WITH CIRCUMFLEX
|
||||
U+00FF # LATIN SMALL LETTER Y WITH DIAERESIS
|
||||
U+007A # LATIN SMALL LETTER Z
|
||||
U+017A # LATIN SMALL LETTER Z WITH ACUTE
|
||||
U+017E # LATIN SMALL LETTER Z WITH CARON
|
||||
U+017C # LATIN SMALL LETTER Z WITH DOT ABOVE
|
||||
U+0292 # LATIN SMALL LETTER EZH
|
||||
U+01EF # LATIN SMALL LETTER EZH WITH CARON
|
||||
U+00FE # LATIN SMALL LETTER THORN
|
||||
|
||||
7
core/src/main/java/google/registry/idn/README.md
Normal file
7
core/src/main/java/google/registry/idn/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# IDN tables
|
||||
|
||||
This directory contains the most recent version of our approved IDN tables. They
|
||||
should match what is published (or will be published soon) by IANA. In the case
|
||||
of IDN tables that have had multiple revisions over time, this directory should
|
||||
contain only the latest version, not previous ones, even when said previous ones
|
||||
might still be in use by previously launched TLDs.
|
||||
@@ -35,7 +35,7 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
@@ -73,7 +73,7 @@ public final class EppResourceUtils {
|
||||
|
||||
/** Returns the full domain repoId in the format HEX-TLD for the specified long id and tld. */
|
||||
public static String createDomainRepoId(long repoId, String tld) {
|
||||
return createRepoId(repoId, Registry.get(tld).getRoidSuffix());
|
||||
return createRepoId(repoId, Tld.get(tld).getRoidSuffix());
|
||||
}
|
||||
|
||||
/** Returns the full repoId in the format HEX-TLD for the specified long id and ROID suffix. */
|
||||
|
||||
@@ -17,8 +17,8 @@ package google.registry.model;
|
||||
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 google.registry.model.tld.Registry.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Registry.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
@@ -33,9 +33,9 @@ import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -121,9 +121,9 @@ public final class OteAccountBuilder {
|
||||
ImmutableMap.of(CurrencyUnit.USD, "123", CurrencyUnit.JPY, "456");
|
||||
|
||||
private final ImmutableMap<String, String> registrarIdToTld;
|
||||
private final Registry sunriseTld;
|
||||
private final Registry gaTld;
|
||||
private final Registry eapTld;
|
||||
private final Tld sunriseTld;
|
||||
private final Tld gaTld;
|
||||
private final Tld eapTld;
|
||||
private final ImmutableList.Builder<RegistrarPoc> contactsBuilder = new ImmutableList.Builder<>();
|
||||
|
||||
private ImmutableList<Registrar> registrars;
|
||||
@@ -247,7 +247,7 @@ public final class OteAccountBuilder {
|
||||
/** Saves all the OT&E entities we created. */
|
||||
private void saveAllEntities() {
|
||||
// use ImmutableObject instead of Registry so that the Key generation doesn't break
|
||||
ImmutableList<Registry> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
|
||||
ImmutableList<Tld> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
|
||||
ImmutableList<RegistrarPoc> contacts = contactsBuilder.build();
|
||||
|
||||
tm().transact(
|
||||
@@ -255,8 +255,7 @@ public final class OteAccountBuilder {
|
||||
if (!replaceExisting) {
|
||||
ImmutableList<VKey<? extends ImmutableObject>> keys =
|
||||
Streams.concat(
|
||||
registries.stream()
|
||||
.map(registry -> Registry.createVKey(registry.getTldStr())),
|
||||
registries.stream().map(tld -> Tld.createVKey(tld.getTldStr())),
|
||||
registrars.stream().map(Registrar::createVKey),
|
||||
contacts.stream().map(RegistrarPoc::createVKey))
|
||||
.collect(toImmutableList());
|
||||
@@ -291,16 +290,13 @@ public final class OteAccountBuilder {
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Registry createTld(
|
||||
String tldName,
|
||||
TldState initialTldState,
|
||||
boolean isEarlyAccess,
|
||||
int roidSuffix) {
|
||||
private static Tld createTld(
|
||||
String tldName, TldState initialTldState, boolean isEarlyAccess, int roidSuffix) {
|
||||
String tldNameAlphaNumerical = tldName.replaceAll("[^a-z\\d]", "");
|
||||
Optional<PremiumList> premiumList = PremiumListDao.getLatestRevision(DEFAULT_PREMIUM_LIST);
|
||||
checkState(premiumList.isPresent(), "Couldn't find premium list %s.", DEFAULT_PREMIUM_LIST);
|
||||
Registry.Builder builder =
|
||||
new Registry.Builder()
|
||||
Tld.Builder builder =
|
||||
new Tld.Builder()
|
||||
.setTldStr(tldName)
|
||||
.setPremiumPricingEngine(StaticPremiumListPricingEngine.NAME)
|
||||
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, initialTldState))
|
||||
|
||||
@@ -22,7 +22,7 @@ import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.model.UpdateAutoTimestampEntity;
|
||||
import google.registry.model.common.Cursor.CursorId;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import javax.persistence.AttributeOverride;
|
||||
@@ -134,7 +134,7 @@ public class Cursor extends UpdateAutoTimestampEntity {
|
||||
return createVKey(type, GLOBAL);
|
||||
}
|
||||
|
||||
public static VKey<Cursor> createScopedVKey(CursorType type, Registry tld) {
|
||||
public static VKey<Cursor> createScopedVKey(CursorType type, Tld tld) {
|
||||
return createVKey(type, tld.getTldStr());
|
||||
}
|
||||
|
||||
@@ -172,8 +172,8 @@ public class Cursor extends UpdateAutoTimestampEntity {
|
||||
return create(cursorType, cursorTime, GLOBAL);
|
||||
}
|
||||
|
||||
/** Creates a new cursor instance with a given {@link Registry} scope. */
|
||||
public static Cursor createScoped(CursorType cursorType, DateTime cursorTime, Registry scope) {
|
||||
/** Creates a new cursor instance with a given {@link Tld} scope. */
|
||||
public static Cursor createScoped(CursorType cursorType, DateTime cursorTime, Tld scope) {
|
||||
checkNotNull(scope, "Cursor scope cannot be null");
|
||||
return create(cursorType, cursorTime, scope.getTldStr());
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ 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 google.registry.persistence.VKey;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
@@ -89,6 +90,11 @@ public class DnsRefreshRequest extends ImmutableObject {
|
||||
return lastProcessTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<DnsRefreshRequest> createVKey() {
|
||||
return VKey.create(DnsRefreshRequest.class, id);
|
||||
}
|
||||
|
||||
protected DnsRefreshRequest() {}
|
||||
|
||||
private DnsRefreshRequest(
|
||||
|
||||
@@ -56,7 +56,7 @@ import google.registry.model.host.Host;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.poll.PollMessage.Autorenew;
|
||||
import google.registry.model.poll.PollMessage.OneTime;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -488,7 +488,7 @@ public class DomainBase extends EppResource
|
||||
GracePeriodStatus.TRANSFER,
|
||||
domain.getRepoId(),
|
||||
transferExpirationTime.plus(
|
||||
Registry.get(domain.getTld()).getTransferGracePeriodLength()),
|
||||
Tld.get(domain.getTld()).getTransferGracePeriodLength()),
|
||||
transferData.getGainingRegistrarId(),
|
||||
transferData.getServerApproveBillingEvent())));
|
||||
} else {
|
||||
@@ -523,8 +523,7 @@ public class DomainBase extends EppResource
|
||||
GracePeriod.createForRecurring(
|
||||
GracePeriodStatus.AUTO_RENEW,
|
||||
domain.getRepoId(),
|
||||
lastAutorenewTime.plus(
|
||||
Registry.get(domain.getTld()).getAutoRenewGracePeriodLength()),
|
||||
lastAutorenewTime.plus(Tld.get(domain.getTld()).getAutoRenewGracePeriodLength()),
|
||||
domain.getCurrentSponsorRegistrarId(),
|
||||
domain.getAutorenewBillingEvent()));
|
||||
newLastEppUpdateTime = Optional.of(lastAutorenewTime);
|
||||
|
||||
@@ -34,7 +34,7 @@ public class Credit extends BaseFee {
|
||||
BigDecimal cost, FeeType type, String description) {
|
||||
Credit instance = new Credit();
|
||||
instance.cost = checkNotNull(cost);
|
||||
checkArgument(instance.cost.signum() < 0);
|
||||
checkArgument(instance.cost.signum() <= 0, "A credit cannot have a positive cost");
|
||||
instance.type = checkNotNull(type);
|
||||
instance.description = description;
|
||||
return instance;
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -35,14 +35,14 @@ public final class StaticPremiumListPricingEngine implements PremiumPricingEngin
|
||||
|
||||
@Override
|
||||
public DomainPrices getDomainPrices(String domainName, DateTime priceTime) {
|
||||
String tld = getTldFromDomainName(domainName);
|
||||
String tldStr = getTldFromDomainName(domainName);
|
||||
String label = InternetDomainName.from(domainName).parts().get(0);
|
||||
Registry registry = Registry.get(checkNotNull(tld, "tld"));
|
||||
Tld tld = Tld.get(checkNotNull(tldStr, "tld"));
|
||||
Optional<Money> premiumPrice =
|
||||
registry.getPremiumListName().flatMap(pl -> PremiumListDao.getPremiumPrice(pl, label));
|
||||
tld.getPremiumListName().flatMap(pl -> PremiumListDao.getPremiumPrice(pl, label));
|
||||
return DomainPrices.create(
|
||||
premiumPrice.isPresent(),
|
||||
premiumPrice.orElse(registry.getStandardCreateCost()),
|
||||
premiumPrice.orElse(registry.getStandardRenewCost(priceTime)));
|
||||
premiumPrice.orElse(tld.getStandardCreateCost()),
|
||||
premiumPrice.orElse(tld.getStandardRenewCost(priceTime)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ import google.registry.model.JsonMapBuilder;
|
||||
import google.registry.model.Jsonifiable;
|
||||
import google.registry.model.UpdateAutoTimestamp;
|
||||
import google.registry.model.UpdateAutoTimestampEntity;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
@@ -723,11 +723,11 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* to set the allowed TLDs.
|
||||
*/
|
||||
public Builder setAllowedTldsUncached(Set<String> allowedTlds) {
|
||||
ImmutableSet<VKey<Registry>> newTldKeys =
|
||||
ImmutableSet<VKey<Tld>> newTldKeys =
|
||||
Sets.difference(allowedTlds, getInstance().getAllowedTlds()).stream()
|
||||
.map(Registry::createVKey)
|
||||
.map(Tld::createVKey)
|
||||
.collect(toImmutableSet());
|
||||
Set<VKey<Registry>> missingTldKeys =
|
||||
Set<VKey<Tld>> missingTldKeys =
|
||||
Sets.difference(
|
||||
newTldKeys, tm().transact(() -> tm().loadByKeysIfPresent(newTldKeys)).keySet());
|
||||
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys);
|
||||
@@ -903,10 +903,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
// In order to grant access to real TLDs, the registrar must have a corresponding billing
|
||||
// account ID for that TLD's billing currency.
|
||||
ImmutableSet<String> nonBillableTlds =
|
||||
Registry.get(getInstance().getAllowedTlds()).stream()
|
||||
Tld.get(getInstance().getAllowedTlds()).stream()
|
||||
.filter(r -> r.getTldType() == TldType.REAL)
|
||||
.filter(r -> !getInstance().getBillingAccountMap().containsKey(r.getCurrency()))
|
||||
.map(Registry::getTldStr)
|
||||
.map(Tld::getTldStr)
|
||||
.collect(toImmutableSet());
|
||||
checkArgument(
|
||||
nonBillableTlds.isEmpty(),
|
||||
|
||||
@@ -32,14 +32,14 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/** Utilities for finding and listing {@link Registry} entities. */
|
||||
/** Utilities for finding and listing {@link Tld} entities. */
|
||||
public final class Registries {
|
||||
|
||||
private Registries() {}
|
||||
@@ -85,8 +85,8 @@ public final class Registries {
|
||||
}
|
||||
|
||||
/** Returns the Registry entities themselves of the given type loaded fresh from the database. */
|
||||
public static ImmutableSet<Registry> getTldEntitiesOfType(TldType type) {
|
||||
return Registry.get(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
public static ImmutableSet<Tld> getTldEntitiesOfType(TldType type) {
|
||||
return Tld.get(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
}
|
||||
|
||||
/** Pass-through check that the specified TLD exists, otherwise throw an IAE. */
|
||||
|
||||
@@ -52,6 +52,7 @@ import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.converter.JodaMoneyType;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
import google.registry.util.Idn;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -75,12 +76,12 @@ import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Persisted per-TLD configuration data. */
|
||||
@Entity(name = "Tld")
|
||||
public class Registry extends ImmutableObject implements Buildable, UnsafeSerializable {
|
||||
@Entity
|
||||
public class Tld extends ImmutableObject implements Buildable, UnsafeSerializable {
|
||||
|
||||
/**
|
||||
* The canonical string representation of the TLD associated with this {@link Registry}, which is
|
||||
* the standard ASCII for regular TLDs and punycoded ASCII for IDN TLDs.
|
||||
* The canonical string representation of the TLD associated with this {@link Tld}, which is the
|
||||
* standard ASCII for regular TLDs and punycoded ASCII for IDN TLDs.
|
||||
*/
|
||||
@Id
|
||||
@Column(name = "tld_name", nullable = false)
|
||||
@@ -131,7 +132,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
*/
|
||||
public enum TldState {
|
||||
|
||||
/** The state of not yet being delegated to this registry in the root zone by IANA. */
|
||||
/** The state of not yet being delegated to this TLD in the root zone by IANA. */
|
||||
PREDELEGATION,
|
||||
|
||||
/**
|
||||
@@ -157,19 +158,19 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
PDT
|
||||
}
|
||||
|
||||
/** Returns the registry for a given TLD, throwing if none exists. */
|
||||
public static Registry get(String tld) {
|
||||
Registry maybeRegistry = CACHE.get(tld);
|
||||
if (maybeRegistry == null) {
|
||||
throw new RegistryNotFoundException(tld);
|
||||
/** Returns the TLD for a given TLD, throwing if none exists. */
|
||||
public static Tld get(String tld) {
|
||||
Tld maybeTld = CACHE.get(tld);
|
||||
if (maybeTld == null) {
|
||||
throw new TldNotFoundException(tld);
|
||||
} else {
|
||||
return maybeRegistry;
|
||||
return maybeTld;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the registry entities for the given TLD strings, throwing if any don't exist. */
|
||||
public static ImmutableSet<Registry> get(Set<String> tlds) {
|
||||
Map<String, Registry> registries = CACHE.getAll(tlds);
|
||||
/** Returns the TLD entities for the given TLD strings, throwing if any don't exist. */
|
||||
public static ImmutableSet<Tld> get(Set<String> tlds) {
|
||||
Map<String, Tld> registries = CACHE.getAll(tlds);
|
||||
ImmutableSet<String> missingRegistries =
|
||||
registries.entrySet().stream()
|
||||
.filter(e -> e.getValue() == null)
|
||||
@@ -178,48 +179,48 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
if (missingRegistries.isEmpty()) {
|
||||
return registries.values().stream().collect(toImmutableSet());
|
||||
} else {
|
||||
throw new RegistryNotFoundException(missingRegistries);
|
||||
throw new TldNotFoundException(missingRegistries);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the cache entry.
|
||||
*
|
||||
* <p>This is called automatically when the registry is saved. One should also call it when a
|
||||
* registry is deleted.
|
||||
* <p>This is called automatically when the tld is saved. One should also call it when a tld is
|
||||
* deleted.
|
||||
*/
|
||||
@PostPersist
|
||||
public void invalidateInCache() {
|
||||
CACHE.invalidate(tldStr);
|
||||
}
|
||||
|
||||
/** A cache that loads the {@link Registry} for a given tld. */
|
||||
private static final LoadingCache<String, Registry> CACHE =
|
||||
/** A cache that loads the {@link Tld} for a given tld. */
|
||||
private static final LoadingCache<String, Tld> CACHE =
|
||||
CacheUtils.newCacheBuilder(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<String, Registry>() {
|
||||
new CacheLoader<String, Tld>() {
|
||||
@Override
|
||||
public Registry load(final String tld) {
|
||||
public Tld load(final String tld) {
|
||||
return tm().transact(() -> tm().loadByKeyIfPresent(createVKey(tld))).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Registry> loadAll(Iterable<? extends String> tlds) {
|
||||
ImmutableMap<String, VKey<Registry>> keysMap =
|
||||
toMap(ImmutableSet.copyOf(tlds), Registry::createVKey);
|
||||
Map<VKey<? extends Registry>, Registry> entities =
|
||||
public Map<String, Tld> loadAll(Iterable<? extends String> tlds) {
|
||||
ImmutableMap<String, VKey<Tld>> keysMap =
|
||||
toMap(ImmutableSet.copyOf(tlds), Tld::createVKey);
|
||||
Map<VKey<? extends Tld>, Tld> entities =
|
||||
tm().transact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
return Maps.transformEntries(keysMap, (k, v) -> entities.getOrDefault(v, null));
|
||||
}
|
||||
});
|
||||
|
||||
public static VKey<Registry> createVKey(String tld) {
|
||||
return VKey.create(Registry.class, tld);
|
||||
public static VKey<Tld> createVKey(String tld) {
|
||||
return VKey.create(Tld.class, tld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<Registry> createVKey() {
|
||||
return VKey.create(Registry.class, tldStr);
|
||||
public VKey<Tld> createVKey() {
|
||||
return VKey.create(Tld.class, tldStr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,7 +248,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
/**
|
||||
* The number of locks we allow at once for {@link google.registry.dns.PublishDnsUpdatesAction}.
|
||||
*
|
||||
* <p>This should always be a positive integer -- use 1 for TLD-wide locks. All {@link Registry}
|
||||
* <p>This should always be a positive integer -- use 1 for TLD-wide locks. All {@link Tld}
|
||||
* objects have this value default to 1.
|
||||
*
|
||||
* <p>WARNING: changing this parameter changes the lock name for subsequent DNS updates, and thus
|
||||
@@ -258,7 +259,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
* <ol>
|
||||
* <li>Pause the DNS queue via {@link google.registry.tools.UpdateTldCommand}
|
||||
* <li>Change this number
|
||||
* <li>Let the Registry caches expire (currently 5 minutes) and drain the DNS publish queue
|
||||
* <li>Let the Tld caches expire (currently 5 minutes) and drain the DNS publish queue
|
||||
* <li>Unpause the DNS queue
|
||||
* </ol>
|
||||
*
|
||||
@@ -296,7 +297,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
*/
|
||||
Duration dnsDsTtl;
|
||||
/**
|
||||
* The unicode-aware representation of the TLD associated with this {@link Registry}.
|
||||
* The unicode-aware representation of the TLD associated with this {@link Tld}.
|
||||
*
|
||||
* <p>This will be equal to {@link #tldStr} for ASCII TLDs, but will be non-ASCII for IDN TLDs. We
|
||||
* store this in a field so that it will be retained upon import into BigQuery.
|
||||
@@ -334,7 +335,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
@Column(nullable = false)
|
||||
CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
/** The set of reserved list names that are applicable to this registry. */
|
||||
/** The set of reserved list names that are applicable to this tld. */
|
||||
@Column(name = "reserved_list_names")
|
||||
Set<String> reservedListNames;
|
||||
|
||||
@@ -343,8 +344,8 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
*
|
||||
* <p>This set contains only the names of the list and not a reference to the lists. Updates to a
|
||||
* reserved list in Cloud SQL are saved as a new ReservedList entity. When using the ReservedList
|
||||
* for a registry, the database should be queried for the entity with this name that has the
|
||||
* largest revision ID.
|
||||
* for a tld, the database should be queried for the entity with this name that has the largest
|
||||
* revision ID.
|
||||
*/
|
||||
public ImmutableSet<String> getReservedListNames() {
|
||||
return nullToEmptyImmutableCopy(reservedListNames);
|
||||
@@ -354,9 +355,8 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
* The name of the {@link PremiumList} for this TLD, if there is one.
|
||||
*
|
||||
* <p>This is only the name of the list and not a reference to the list. Updates to the premium
|
||||
* list in Cloud SQL are saved as a new PremiumList entity. When using the PremiumList for a
|
||||
* registry, the database should be queried for the entity with this name that has the largest
|
||||
* revision ID.
|
||||
* list in Cloud SQL are saved as a new PremiumList entity. When using the PremiumList for a tld,
|
||||
* the database should be queried for the entity with this name that has the largest revision ID.
|
||||
*/
|
||||
@Column(name = "premium_list_name")
|
||||
String premiumListName;
|
||||
@@ -487,6 +487,9 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
*/
|
||||
List<VKey<AllocationToken>> defaultPromoTokens;
|
||||
|
||||
/** A set of allowed {@link IdnTableEnum}s for this TLD, or empty if we should use the default. */
|
||||
Set<IdnTableEnum> idnTables;
|
||||
|
||||
public String getTldStr() {
|
||||
return tldStr;
|
||||
}
|
||||
@@ -587,8 +590,8 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the add-on cost of a domain restore (the flat registry-wide fee charged in addition to
|
||||
* one year of renewal for that name).
|
||||
* Returns the add-on cost of a domain restore (the flat tld-wide fee charged in addition to one
|
||||
* year of renewal for that name).
|
||||
*/
|
||||
public Money getStandardRestoreCost() {
|
||||
return restoreBillingCost;
|
||||
@@ -621,7 +624,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return renewBillingCostTransitions.toValueMap();
|
||||
}
|
||||
|
||||
/** Returns the EAP fee for the registry at the given time. */
|
||||
/** Returns the EAP fee for the tld at the given time. */
|
||||
public Fee getEapFeeFor(DateTime now) {
|
||||
ImmutableSortedMap<DateTime, Money> valueMap = getEapFeeScheduleAsMap();
|
||||
DateTime periodStart = valueMap.floorKey(now);
|
||||
@@ -694,16 +697,20 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return nullToEmptyImmutableCopy(defaultPromoTokens);
|
||||
}
|
||||
|
||||
public ImmutableSet<IdnTableEnum> getIdnTables() {
|
||||
return nullToEmptyImmutableCopy(idnTables);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link Registry} objects, since they are immutable. */
|
||||
public static class Builder extends Buildable.Builder<Registry> {
|
||||
/** A builder for constructing {@link Tld} objects, since they are immutable. */
|
||||
public static class Builder extends Buildable.Builder<Tld> {
|
||||
public Builder() {}
|
||||
|
||||
private Builder(Registry instance) {
|
||||
private Builder(Tld instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
@@ -777,13 +784,13 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsNsAtl(Duration dnsNsAtl) {
|
||||
getInstance().dnsNsTtl = dnsNsAtl;
|
||||
public Builder setDnsNsTtl(Duration dnsNsTtl) {
|
||||
getInstance().dnsNsTtl = dnsNsTtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDnsDsAtl(Duration dnsDsAtl) {
|
||||
getInstance().dnsDsTtl = dnsDsAtl;
|
||||
public Builder setDnsDsTtl(Duration dnsDsTtl) {
|
||||
getInstance().dnsDsTtl = dnsDsTtl;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -992,9 +999,14 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setIdnTables(ImmutableSet<IdnTableEnum> idnTables) {
|
||||
getInstance().idnTables = idnTables;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Registry build() {
|
||||
final Registry instance = getInstance();
|
||||
public Tld build() {
|
||||
final Tld instance = getInstance();
|
||||
// Pick up the name of the associated TLD from the instance object.
|
||||
String tldName = instance.tldStr;
|
||||
checkArgument(tldName != null, "No registry TLD specified");
|
||||
@@ -1006,7 +1018,7 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
// Check the validity of all TimedTransitionProperties to ensure that they have values for
|
||||
// START_OF_TIME. The setters above have already checked this for new values, but also check
|
||||
// here to catch cases where we loaded an invalid TimedTransitionProperty from the database
|
||||
// and cloned it into a new builder, to block re-building a Registry in an invalid state.
|
||||
// and cloned it into a new builder, to block re-building a Tld in an invalid state.
|
||||
instance.tldStateTransitions.checkValidity();
|
||||
instance.renewBillingCostTransitions.checkValidity();
|
||||
instance.eapFeeSchedule.checkValidity();
|
||||
@@ -1014,24 +1026,24 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
|
||||
checkArgument(
|
||||
instance.getStandardCreateCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Create cost must be in the registry's currency");
|
||||
"Create cost must be in the tld's currency");
|
||||
checkArgument(
|
||||
instance.getStandardRestoreCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Restore cost must be in the registry's currency");
|
||||
"Restore cost must be in the TLD's currency");
|
||||
checkArgument(
|
||||
instance.getServerStatusChangeCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Server status change cost must be in the registry's currency");
|
||||
"Server status change cost must be in the TLD's currency");
|
||||
checkArgument(
|
||||
instance.getRegistryLockOrUnlockBillingCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Registry lock/unlock cost must be in the registry's currency");
|
||||
"Registry lock/unlock cost must be in the TLD's currency");
|
||||
Predicate<Money> currencyCheck =
|
||||
(Money money) -> money.getCurrencyUnit().equals(instance.currency);
|
||||
checkArgument(
|
||||
instance.getRenewBillingCostTransitions().values().stream().allMatch(currencyCheck),
|
||||
"Renew cost must be in the registry's currency");
|
||||
"Renew cost must be in the TLD's currency");
|
||||
checkArgument(
|
||||
instance.eapFeeSchedule.toValueMap().values().stream().allMatch(currencyCheck),
|
||||
"All EAP fees must be in the registry's currency");
|
||||
"All EAP fees must be in the TLD's currency");
|
||||
checkArgumentNotNull(
|
||||
instance.pricingEngineClassName, "All registries must have a configured pricing engine");
|
||||
checkArgument(
|
||||
@@ -1049,14 +1061,14 @@ public class Registry extends ImmutableObject implements Buildable, UnsafeSerial
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception to throw when no Registry entity is found for given TLD string(s). */
|
||||
public static class RegistryNotFoundException extends RuntimeException {
|
||||
/** Exception to throw when no Tld entity is found for given TLD string(s). */
|
||||
public static class TldNotFoundException extends RuntimeException {
|
||||
|
||||
RegistryNotFoundException(ImmutableSet<String> tlds) {
|
||||
super("No registry object(s) found for " + Joiner.on(", ").join(tlds));
|
||||
TldNotFoundException(ImmutableSet<String> tlds) {
|
||||
super("No TLD object(s) found for " + Joiner.on(", ").join(tlds));
|
||||
}
|
||||
|
||||
RegistryNotFoundException(String tld) {
|
||||
TldNotFoundException(String tld) {
|
||||
this(ImmutableSet.of(tld));
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multiset;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -138,11 +138,11 @@ public abstract class BaseDomainLabelList<T extends Comparable<?>, R extends Dom
|
||||
/** Gets the names of the tlds that reference this list. */
|
||||
public final ImmutableSet<String> getReferencingTlds() {
|
||||
return getTlds().stream()
|
||||
.filter((tld) -> refersToList(Registry.get(tld), name))
|
||||
.filter((tld) -> refersToList(Tld.get(tld), name))
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
|
||||
protected abstract boolean refersToList(Registry registry, String name);
|
||||
protected abstract boolean refersToList(Tld tld, String name);
|
||||
|
||||
/** Base builder for derived classes of {@link BaseDomainLabelList}. */
|
||||
public abstract static class Builder<T extends BaseDomainLabelList<?, ?>, B extends Builder<T, ?>>
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.hash.BloomFilter;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.PremiumList.PremiumEntry;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
@@ -200,8 +200,8 @@ public final class PremiumList extends BaseDomainLabelList<BigDecimal, PremiumEn
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean refersToList(Registry registry, String name) {
|
||||
return Objects.equals(registry.getPremiumListName().orElse(null), name);
|
||||
public boolean refersToList(Tld tld, String name) {
|
||||
return Objects.equals(tld.getPremiumListName().orElse(null), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.DomainLabelMetrics.MetricsReservedListMatch;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
@@ -173,8 +173,8 @@ public final class ReservedList
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refersToList(Registry registry, String name) {
|
||||
return registry.getReservedListNames().contains(name);
|
||||
protected boolean refersToList(Tld tld, String name) {
|
||||
return tld.getReservedListNames().contains(name);
|
||||
}
|
||||
|
||||
/** Determines whether the ReservedList is in use on any Registry */
|
||||
@@ -243,15 +243,16 @@ public final class ReservedList
|
||||
* Helper function to retrieve the entries associated with this label and TLD, or an empty set if
|
||||
* no such entry exists.
|
||||
*/
|
||||
private static ImmutableSet<ReservedListEntry> getReservedListEntries(String label, String tld) {
|
||||
private static ImmutableSet<ReservedListEntry> getReservedListEntries(
|
||||
String label, String tldStr) {
|
||||
DateTime startTime = DateTime.now(UTC);
|
||||
Registry registry = Registry.get(checkNotNull(tld, "tld must not be null"));
|
||||
Tld tld = Tld.get(checkNotNull(tldStr, "tld must not be null"));
|
||||
ImmutableSet.Builder<ReservedListEntry> entriesBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<MetricsReservedListMatch> metricMatchesBuilder =
|
||||
new ImmutableSet.Builder<>();
|
||||
|
||||
// Loop through all reservation lists and add each of them.
|
||||
for (ReservedList rl : loadReservedLists(registry.getReservedListNames())) {
|
||||
for (ReservedList rl : loadReservedLists(tld.getReservedListNames())) {
|
||||
if (rl.getReservedListEntries().containsKey(label)) {
|
||||
ReservedListEntry entry = rl.getReservedListEntries().get(label);
|
||||
entriesBuilder.add(entry);
|
||||
@@ -261,7 +262,9 @@ public final class ReservedList
|
||||
}
|
||||
ImmutableSet<ReservedListEntry> entries = entriesBuilder.build();
|
||||
DomainLabelMetrics.recordReservedListCheckOutcome(
|
||||
tld, metricMatchesBuilder.build(), DateTime.now(UTC).getMillis() - startTime.getMillis());
|
||||
tldStr,
|
||||
metricMatchesBuilder.build(),
|
||||
DateTime.now(UTC).getMillis() - startTime.getMillis());
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import google.registry.cron.TldFanoutAction;
|
||||
import google.registry.dns.DnsModule;
|
||||
import google.registry.dns.PublishDnsUpdatesAction;
|
||||
import google.registry.dns.ReadDnsQueueAction;
|
||||
import google.registry.dns.ReadDnsRefreshRequestsAction;
|
||||
import google.registry.dns.RefreshDnsAction;
|
||||
import google.registry.dns.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.dns.writer.VoidDnsWriterModule;
|
||||
@@ -144,6 +145,8 @@ interface BackendRequestComponent {
|
||||
|
||||
ReadDnsQueueAction readDnsQueueAction();
|
||||
|
||||
ReadDnsRefreshRequestsAction readDnsRefreshRequestsAction();
|
||||
|
||||
RdeReportAction rdeReportAction();
|
||||
|
||||
RdeStagingAction rdeStagingAction();
|
||||
|
||||
@@ -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.tldconfig.idn.IdnTableEnum;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA {@link AttributeConverter} for storing/retrieving {@link IdnTableEnum}s. */
|
||||
@Converter(autoApply = true)
|
||||
public class IdnTableEnumSetConverter extends StringSetConverterBase<IdnTableEnum> {
|
||||
|
||||
@Override
|
||||
String toString(IdnTableEnum element) {
|
||||
return element.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
IdnTableEnum fromString(String value) {
|
||||
return IdnTableEnum.valueOf(value);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.persistence.converter;
|
||||
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA converter for storing/retrieving {@code TimedTransitionProperty<TldState>} objects. */
|
||||
|
||||
@@ -154,6 +154,10 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
|
||||
@Override
|
||||
public <T> T transact(Supplier<T> work) {
|
||||
// This prevents inner transaction from retrying, thus avoiding a cascade retry effect.
|
||||
if (inTransaction()) {
|
||||
return transactNoRetry(work);
|
||||
}
|
||||
return retrier.callWithRetry(() -> transactNoRetry(work), JpaRetries::isFailedTxnRetriable);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import static google.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
|
||||
import google.registry.model.pricing.PremiumPricingEngine;
|
||||
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.Map;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -58,7 +58,7 @@ public final class PricingEngineProxy {
|
||||
*/
|
||||
public static DomainPrices getPricesForDomainName(String domainName, DateTime priceTime) {
|
||||
String tld = getTldFromDomainName(domainName);
|
||||
String clazz = Registry.get(tld).getPremiumPricingEngineClassName();
|
||||
String clazz = Tld.get(tld).getPremiumPricingEngineClassName();
|
||||
PremiumPricingEngine engine = premiumPricingEngines.get(clazz);
|
||||
checkState(engine != null, "Could not load pricing engine %s for TLD %s", clazz, tld);
|
||||
return engine.getDomainPrices(domainName, priceTime);
|
||||
|
||||
@@ -30,7 +30,7 @@ import google.registry.keyring.api.KeyModule.Key;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.rde.RdeNamingUtils;
|
||||
import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.NoContentException;
|
||||
import google.registry.request.Parameter;
|
||||
@@ -97,8 +97,7 @@ public final class BrdaCopyAction implements Runnable {
|
||||
// TODO(b/217772483): consider guarding this action with a lock and check if there is work.
|
||||
// Not urgent since file writes on GCS are atomic.
|
||||
Optional<Cursor> cursor =
|
||||
tm().transact(
|
||||
() -> tm().loadByKeyIfPresent(Cursor.createScopedVKey(BRDA, Registry.get(tld))));
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(Cursor.createScopedVKey(BRDA, Tld.get(tld))));
|
||||
DateTime brdaCursorTime = getCursorTimeOrStartOfTime(cursor);
|
||||
if (isBeforeOrAt(brdaCursorTime, watermark)) {
|
||||
throw new NoContentException(
|
||||
|
||||
@@ -19,7 +19,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.HttpException.NoContentException;
|
||||
import google.registry.request.HttpException.ServiceUnavailableException;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
@@ -74,24 +74,23 @@ class EscrowTaskRunner {
|
||||
* Acquires lock, checks cursor, invokes {@code task}, and advances cursor.
|
||||
*
|
||||
* @param task the task to run
|
||||
* @param registry the {@link Registry} that we are performing escrow for
|
||||
* @param tld the {@link Tld} that we are performing escrow for
|
||||
* @param timeout time when we assume failure, kill the task (and instance) and release the lock
|
||||
* @param cursorType the cursor to advance on success, indicating the next required runtime
|
||||
* @param interval how far to advance the cursor (e.g. a day for RDE, a week for BRDA)
|
||||
*/
|
||||
void lockRunAndRollForward(
|
||||
final EscrowTask task,
|
||||
final Registry registry,
|
||||
final Tld tld,
|
||||
Duration timeout,
|
||||
final CursorType cursorType,
|
||||
final Duration interval) {
|
||||
Callable<Void> lockRunner =
|
||||
() -> {
|
||||
logger.atInfo().log("Performing escrow for TLD '%s'.", registry.getTld());
|
||||
logger.atInfo().log("Performing escrow for TLD '%s'.", tld.getTld());
|
||||
DateTime startOfToday = clock.nowUtc().withTimeAtStartOfDay();
|
||||
DateTime nextRequiredRun =
|
||||
tm().transact(
|
||||
() -> tm().loadByKeyIfPresent(Cursor.createScopedVKey(cursorType, registry)))
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(Cursor.createScopedVKey(cursorType, tld)))
|
||||
.map(Cursor::getCursorTime)
|
||||
.orElse(startOfToday);
|
||||
if (nextRequiredRun.isAfter(startOfToday)) {
|
||||
@@ -101,16 +100,16 @@ class EscrowTaskRunner {
|
||||
task.runWithLock(nextRequiredRun);
|
||||
DateTime nextRun = nextRequiredRun.plus(interval);
|
||||
logger.atInfo().log("Rolling cursor forward to %s.", nextRun);
|
||||
tm().transact(() -> tm().put(Cursor.createScoped(cursorType, nextRun, registry)));
|
||||
tm().transact(() -> tm().put(Cursor.createScoped(cursorType, nextRun, tld)));
|
||||
return null;
|
||||
};
|
||||
String lockName = String.format("EscrowTaskRunner %s", task.getClass().getSimpleName());
|
||||
if (!lockHandler.executeWithLocks(lockRunner, registry.getTldStr(), timeout, lockName)) {
|
||||
if (!lockHandler.executeWithLocks(lockRunner, tld.getTldStr(), timeout, lockName)) {
|
||||
// This will happen if either: a) the task is double-executed; b) the task takes a long time
|
||||
// to run and the retry task got executed while the first one is still running. In both
|
||||
// situations the safest thing to do is to just return 503 so the task gets retried later.
|
||||
throw new ServiceUnavailableException(
|
||||
String.format("Lock in use: %s for TLD: %s", lockName, registry.getTldStr()));
|
||||
String.format("Lock in use: %s for TLD: %s", lockName, tld.getTldStr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.rde.RdeMode;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -83,23 +83,22 @@ public final class PendingDepositChecker {
|
||||
ImmutableSetMultimap.Builder<String, PendingDeposit> builder =
|
||||
new ImmutableSetMultimap.Builder<>();
|
||||
DateTime now = clock.nowUtc();
|
||||
for (String tld : Registries.getTldsOfType(TldType.REAL)) {
|
||||
Registry registry = Registry.get(tld);
|
||||
if (!registry.getEscrowEnabled()) {
|
||||
for (String tldStr : Registries.getTldsOfType(TldType.REAL)) {
|
||||
Tld tld = Tld.get(tldStr);
|
||||
if (!tld.getEscrowEnabled()) {
|
||||
continue;
|
||||
}
|
||||
// Avoid creating a transaction unless absolutely necessary.
|
||||
Optional<Cursor> maybeCursor =
|
||||
tm().transact(
|
||||
() -> tm().loadByKeyIfPresent(Cursor.createScopedVKey(cursorType, registry)));
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(Cursor.createScopedVKey(cursorType, tld)));
|
||||
DateTime cursorValue = maybeCursor.map(Cursor::getCursorTime).orElse(startingPoint);
|
||||
if (isBeforeOrAt(cursorValue, now)) {
|
||||
DateTime watermark =
|
||||
maybeCursor
|
||||
.map(Cursor::getCursorTime)
|
||||
.orElse(transactionallyInitializeCursor(registry, cursorType, startingPoint));
|
||||
.orElse(transactionallyInitializeCursor(tld, cursorType, startingPoint));
|
||||
if (isBeforeOrAt(watermark, now)) {
|
||||
builder.put(tld, PendingDeposit.create(tld, watermark, mode, cursorType, interval));
|
||||
builder.put(tldStr, PendingDeposit.create(tldStr, watermark, mode, cursorType, interval));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,15 +106,15 @@ public final class PendingDepositChecker {
|
||||
}
|
||||
|
||||
private DateTime transactionallyInitializeCursor(
|
||||
final Registry registry, final CursorType cursorType, final DateTime initialValue) {
|
||||
final Tld tld, final CursorType cursorType, final DateTime initialValue) {
|
||||
return tm().transact(
|
||||
() -> {
|
||||
Optional<Cursor> maybeCursor =
|
||||
tm().loadByKeyIfPresent(Cursor.createScopedVKey(cursorType, registry));
|
||||
tm().loadByKeyIfPresent(Cursor.createScopedVKey(cursorType, tld));
|
||||
if (maybeCursor.isPresent()) {
|
||||
return maybeCursor.get().getCursorTime();
|
||||
}
|
||||
tm().put(Cursor.createScoped(cursorType, initialValue, registry));
|
||||
tm().put(Cursor.createScoped(cursorType, initialValue, tld));
|
||||
return initialValue;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.rde.RdeNamingUtils;
|
||||
import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.rde.EscrowTaskRunner.EscrowTask;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.NoContentException;
|
||||
@@ -76,7 +76,7 @@ public final class RdeReportAction implements Runnable, EscrowTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
runner.lockRunAndRollForward(this, Registry.get(tld), timeout, CursorType.RDE_REPORT, interval);
|
||||
runner.lockRunAndRollForward(this, Tld.get(tld), timeout, CursorType.RDE_REPORT, interval);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,7 +85,7 @@ public final class RdeReportAction implements Runnable, EscrowTask {
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(
|
||||
Cursor.createScopedVKey(CursorType.RDE_UPLOAD, Registry.get(tld))));
|
||||
Cursor.createScopedVKey(CursorType.RDE_UPLOAD, Tld.get(tld))));
|
||||
DateTime cursorTime = getCursorTimeOrStartOfTime(cursor);
|
||||
if (isBeforeOrAt(cursorTime, watermark)) {
|
||||
throw new NoContentException(
|
||||
|
||||
@@ -46,7 +46,7 @@ import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.rde.RdeNamingUtils;
|
||||
import google.registry.model.rde.RdeRevision;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.rde.EscrowTaskRunner.EscrowTask;
|
||||
import google.registry.rde.JSchSshSession.JSchSshSessionFactory;
|
||||
import google.registry.request.Action;
|
||||
@@ -125,7 +125,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
@Override
|
||||
public void run() {
|
||||
logger.atInfo().log("Attempting to acquire RDE upload lock for TLD '%s'.", tld);
|
||||
runner.lockRunAndRollForward(this, Registry.get(tld), timeout, CursorType.RDE_UPLOAD, interval);
|
||||
runner.lockRunAndRollForward(this, Tld.get(tld), timeout, CursorType.RDE_UPLOAD, interval);
|
||||
HashMultimap<String, String> params = HashMultimap.create();
|
||||
params.put(RequestParameters.PARAM_TLD, tld);
|
||||
prefix.ifPresent(s -> params.put(RdeModule.PARAM_PREFIX, s));
|
||||
@@ -160,9 +160,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
logger.atInfo().log("Verifying readiness to upload the RDE deposit.");
|
||||
Optional<Cursor> cursor =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(
|
||||
Cursor.createScopedVKey(RDE_STAGING, Registry.get(tld))));
|
||||
() -> tm().loadByKeyIfPresent(Cursor.createScopedVKey(RDE_STAGING, Tld.get(tld))));
|
||||
DateTime stagingCursorTime = getCursorTimeOrStartOfTime(cursor);
|
||||
if (isBeforeOrAt(stagingCursorTime, watermark)) {
|
||||
throw new NoContentException(
|
||||
@@ -174,8 +172,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
DateTime sftpCursorTime =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadByKeyIfPresent(
|
||||
Cursor.createScopedVKey(RDE_UPLOAD_SFTP, Registry.get(tld))))
|
||||
tm().loadByKeyIfPresent(Cursor.createScopedVKey(RDE_UPLOAD_SFTP, Tld.get(tld))))
|
||||
.map(Cursor::getCursorTime)
|
||||
.orElse(START_OF_TIME);
|
||||
Duration timeSinceLastSftp = new Duration(sftpCursorTime, clock.nowUtc());
|
||||
@@ -214,7 +211,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
() ->
|
||||
tm().put(
|
||||
Cursor.createScoped(
|
||||
RDE_UPLOAD_SFTP, tm().getTransactionTime(), Registry.get(tld))));
|
||||
RDE_UPLOAD_SFTP, tm().getTransactionTime(), Tld.get(tld))));
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
response.setPayload(String.format("OK %s %s\n", tld, watermark));
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ import google.registry.gcs.GcsUtils;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.HttpException.ServiceUnavailableException;
|
||||
@@ -169,7 +169,7 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||
Cursor.createScoped(
|
||||
cursorType,
|
||||
cursorTime.withTimeAtStartOfDay().withDayOfMonth(1).plusMonths(1),
|
||||
Registry.get(tldStr));
|
||||
Tld.get(tldStr));
|
||||
// In order to keep the transactions short-lived, we load all of the cursors in a single
|
||||
// transaction then later use per-cursor transactions when checking + saving the cursors. We
|
||||
// run behind a lock so the cursors shouldn't be changed, but double check to be sure.
|
||||
@@ -203,11 +203,11 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||
/** Returns a map of each cursor to the tld. */
|
||||
private ImmutableMap<Cursor, String> loadCursors() {
|
||||
|
||||
ImmutableSet<Registry> registries = Registries.getTldEntitiesOfType(TldType.REAL);
|
||||
ImmutableSet<Tld> registries = Registries.getTldEntitiesOfType(TldType.REAL);
|
||||
|
||||
ImmutableMap<VKey<? extends Cursor>, Registry> activityKeyMap =
|
||||
ImmutableMap<VKey<? extends Cursor>, Tld> activityKeyMap =
|
||||
loadKeyMap(registries, CursorType.ICANN_UPLOAD_ACTIVITY);
|
||||
ImmutableMap<VKey<? extends Cursor>, Registry> transactionKeyMap =
|
||||
ImmutableMap<VKey<? extends Cursor>, Tld> transactionKeyMap =
|
||||
loadKeyMap(registries, CursorType.ICANN_UPLOAD_TX);
|
||||
|
||||
ImmutableSet.Builder<VKey<? extends Cursor>> keys = new ImmutableSet.Builder<>();
|
||||
@@ -225,8 +225,8 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||
return cursors.build();
|
||||
}
|
||||
|
||||
private ImmutableMap<VKey<? extends Cursor>, Registry> loadKeyMap(
|
||||
ImmutableSet<Registry> registries, CursorType type) {
|
||||
private ImmutableMap<VKey<? extends Cursor>, Tld> loadKeyMap(
|
||||
ImmutableSet<Tld> registries, CursorType type) {
|
||||
return Maps.uniqueIndex(registries, r -> Cursor.createScopedVKey(type, r));
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ public final class IcannReportingUploadAction implements Runnable {
|
||||
* next month.
|
||||
*/
|
||||
private ImmutableMap<Cursor, String> defaultNullCursorsToNextMonthAndAddToMap(
|
||||
Map<VKey<? extends Cursor>, Registry> keyMap,
|
||||
Map<VKey<? extends Cursor>, Tld> keyMap,
|
||||
CursorType type,
|
||||
Map<VKey<? extends Cursor>, Cursor> cursorMap) {
|
||||
ImmutableMap.Builder<Cursor, String> cursors = new ImmutableMap.Builder<>();
|
||||
|
||||
@@ -17,8 +17,8 @@ package google.registry.tldconfig.idn;
|
||||
import static google.registry.tldconfig.idn.IdnTableEnum.EXTENDED_LATIN;
|
||||
import static google.registry.tldconfig.idn.IdnTableEnum.JA;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.util.Idn;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -26,23 +26,8 @@ import java.util.Optional;
|
||||
public final class IdnLabelValidator {
|
||||
|
||||
/** Most TLDs will use this generic list of IDN tables. */
|
||||
private static final ImmutableList<IdnTableEnum> DEFAULT_IDN_TABLES =
|
||||
ImmutableList.of(EXTENDED_LATIN, JA);
|
||||
|
||||
private static final ImmutableMap<String, ImmutableList<IdnTableEnum>>
|
||||
DEFAULT_IDN_TABLE_LISTS_PER_TLD =
|
||||
ImmutableMap.of("xn--q9jyb4c", ImmutableList.of(EXTENDED_LATIN, JA));
|
||||
|
||||
/** Some TLDs have their own IDN tables, configured here. */
|
||||
private ImmutableMap<String, ImmutableList<IdnTableEnum>> idnTableListsPerTld;
|
||||
|
||||
IdnLabelValidator(ImmutableMap<String, ImmutableList<IdnTableEnum>> indTableListsPerTld) {
|
||||
this.idnTableListsPerTld = indTableListsPerTld;
|
||||
}
|
||||
|
||||
public static IdnLabelValidator createDefaultIdnLabelValidator() {
|
||||
return new IdnLabelValidator(DEFAULT_IDN_TABLE_LISTS_PER_TLD);
|
||||
}
|
||||
private static final ImmutableSet<IdnTableEnum> DEFAULT_IDN_TABLES =
|
||||
ImmutableSet.of(EXTENDED_LATIN, JA);
|
||||
|
||||
/**
|
||||
* Returns name of first matching {@link IdnTable} if domain label is valid for the given TLD.
|
||||
@@ -50,10 +35,13 @@ public final class IdnLabelValidator {
|
||||
* <p>A label is valid if it is considered valid by at least one configured IDN table for that
|
||||
* TLD. If no match is found, an absent value is returned.
|
||||
*/
|
||||
public Optional<String> findValidIdnTableForTld(String label, String tld) {
|
||||
public Optional<String> findValidIdnTableForTld(String label, String tldStr) {
|
||||
String unicodeString = Idn.toUnicode(label);
|
||||
for (IdnTableEnum idnTable :
|
||||
Optional.ofNullable(idnTableListsPerTld.get(tld)).orElse(DEFAULT_IDN_TABLES)) {
|
||||
Tld tld = Tld.get(tldStr); // uses the cache
|
||||
ImmutableSet<IdnTableEnum> idnTablesForTld = tld.getIdnTables();
|
||||
ImmutableSet<IdnTableEnum> idnTables =
|
||||
idnTablesForTld.isEmpty() ? DEFAULT_IDN_TABLES : idnTablesForTld;
|
||||
for (IdnTableEnum idnTable : idnTables) {
|
||||
if (idnTable.getTable().isValidLabel(unicodeString)) {
|
||||
return Optional.of(idnTable.getTable().getName());
|
||||
}
|
||||
|
||||
@@ -24,23 +24,48 @@ import java.net.URL;
|
||||
|
||||
/** Wrapper enum that loads all {@link IdnTable} resources into memory. */
|
||||
public enum IdnTableEnum {
|
||||
EXTENDED_LATIN,
|
||||
JA;
|
||||
|
||||
/**
|
||||
* Extended Latin, as used on our existing TLD launches prior to 2023.
|
||||
*
|
||||
* <p>As of 2023 this table is no longer conformant with ICANN's IDN policies for new launches, so
|
||||
* it is retained solely for legacy compatibility with already-launched TLDs.
|
||||
*/
|
||||
EXTENDED_LATIN("extended_latin.txt"),
|
||||
|
||||
/**
|
||||
* Extended Latin, but with confusable characters removed.
|
||||
*
|
||||
* <p>This is compatible with ICANN's requirements as of 2023, and is used for the Dads and Grads
|
||||
* TLDs and all subsequent TLD launches. Note that confusable characters consist of various
|
||||
* letters with diacritic marks on them, e.g. U+00EF (LATIN SMALL LETTER I WITH DIAERESIS) is not
|
||||
* allowed because it is confusable with the standard i.
|
||||
*/
|
||||
UNCONFUSABLE_LATIN("unconfusable_latin.txt"),
|
||||
|
||||
/**
|
||||
* Japanese, as used on our existing TLD launches prior to 2023.
|
||||
*
|
||||
* <p>As of 2023 this table is no longer conformant with ICANN's IDN policies for new launches, so
|
||||
* it is retained solely for legacy compatibility with already-launched TLDs.
|
||||
*/
|
||||
JA("japanese.txt");
|
||||
|
||||
private final IdnTable table;
|
||||
|
||||
IdnTableEnum() {
|
||||
this.table = load(Ascii.toLowerCase(name()));
|
||||
IdnTableEnum(String filename) {
|
||||
this.table = load(Ascii.toLowerCase(name()), filename);
|
||||
}
|
||||
|
||||
public IdnTable getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
private static IdnTable load(String name) {
|
||||
private static IdnTable load(String tableName, String filename) {
|
||||
try {
|
||||
URL resource = Resources.getResource(IdnTableEnum.class, name + ".txt");
|
||||
return IdnTable.createFrom(name, readLines(resource, UTF_8), LanguageValidator.get(name));
|
||||
URL resource = Resources.getResource(IdnTableEnum.class, filename);
|
||||
return IdnTable.createFrom(
|
||||
tableName, readLines(resource, UTF_8), LanguageValidator.get(tableName));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // should never happen
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# IDN tables
|
||||
|
||||
This directory contains the IDN tables that are actually in use by our running
|
||||
system (and the Java code to enable such). This will include older versions of
|
||||
IDN tables that are no longer published by IANA so long as those tables are
|
||||
still in use.
|
||||
@@ -49,6 +49,7 @@ 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
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# Registry: Charleston Road Registry Inc.
|
||||
# Script: Latn
|
||||
# Version: 2.0
|
||||
# Effective Date: 2023-04-04
|
||||
# URL: https://www.iana.org/domains/idn-tables/tables/google_latn_2.0.txt
|
||||
# Policy: https://www.registry.google/about/policies/domainabuse/
|
||||
# Contact Name: CRR Tech
|
||||
# Email address: crr-tech@google.com
|
||||
# Telephone: +1 (650) 253-0000
|
||||
#
|
||||
# Code points requiring context rules
|
||||
#
|
||||
# Code point Description of rule/Reference
|
||||
#
|
||||
# U+002D Label must neither start nor end with U+002D. Label
|
||||
# HYPHEN-MINUS must not have U+002D in both third and fourth
|
||||
# position. RFC 5891 (sec 4.2.3.1)
|
||||
#
|
||||
|
||||
U+002D # HYPHEN-MINUS
|
||||
U+0030 # DIGIT ZERO
|
||||
U+0031 # DIGIT ONE
|
||||
U+0032 # DIGIT TWO
|
||||
U+0033 # DIGIT THREE
|
||||
U+0034 # DIGIT FOUR
|
||||
U+0035 # DIGIT FIVE
|
||||
U+0036 # DIGIT SIX
|
||||
U+0037 # DIGIT SEVEN
|
||||
U+0038 # DIGIT EIGHT
|
||||
U+0039 # DIGIT NINE
|
||||
U+0061 # LATIN SMALL LETTER A
|
||||
U+00E0 # LATIN SMALL LETTER A WITH GRAVE
|
||||
U+0103 # LATIN SMALL LETTER A WITH BREVE
|
||||
U+00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX
|
||||
U+00E5 # LATIN SMALL LETTER A WITH RING ABOVE
|
||||
U+00E4 # LATIN SMALL LETTER A WITH DIAERESIS
|
||||
U+00E3 # LATIN SMALL LETTER A WITH TILDE
|
||||
U+0105 # LATIN SMALL LETTER A WITH OGONEK
|
||||
U+00E6 # LATIN SMALL LETTER AE
|
||||
U+0062 # LATIN SMALL LETTER B
|
||||
U+0063 # LATIN SMALL LETTER C
|
||||
U+0107 # LATIN SMALL LETTER C WITH ACUTE
|
||||
U+010D # LATIN SMALL LETTER C WITH CARON
|
||||
U+00E7 # LATIN SMALL LETTER C WITH CEDILLA
|
||||
U+0064 # LATIN SMALL LETTER D
|
||||
U+010F # LATIN SMALL LETTER D WITH CARON
|
||||
U+0111 # LATIN SMALL LETTER D WITH STROKE
|
||||
U+00F0 # LATIN SMALL LETTER ETH
|
||||
U+0065 # LATIN SMALL LETTER E
|
||||
U+00E9 # LATIN SMALL LETTER E WITH ACUTE
|
||||
U+00E8 # LATIN SMALL LETTER E WITH GRAVE
|
||||
U+00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX
|
||||
U+011B # LATIN SMALL LETTER E WITH CARON
|
||||
U+00EB # LATIN SMALL LETTER E WITH DIAERESIS
|
||||
U+0119 # LATIN SMALL LETTER E WITH OGONEK
|
||||
U+0113 # LATIN SMALL LETTER E WITH MACRON
|
||||
U+0117 # LATIN SMALL LETTER E WITH DOT ABOVE
|
||||
U+0259 # LATIN SMALL LETTER SCHWA
|
||||
U+0066 # LATIN SMALL LETTER F
|
||||
U+0067 # LATIN SMALL LETTER G
|
||||
U+011F # LATIN SMALL LETTER G WITH BREVE
|
||||
U+0121 # LATIN SMALL LETTER G WITH DOT ABOVE
|
||||
U+0068 # LATIN SMALL LETTER H
|
||||
U+0127 # LATIN SMALL LETTER H WITH STROKE
|
||||
U+0069 # LATIN SMALL LETTER I
|
||||
U+00EC # LATIN SMALL LETTER I WITH GRAVE
|
||||
U+00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX
|
||||
U+012F # LATIN SMALL LETTER I WITH OGONEK
|
||||
U+012B # LATIN SMALL LETTER I WITH MACRON
|
||||
U+006A # LATIN SMALL LETTER J
|
||||
U+006B # LATIN SMALL LETTER K
|
||||
U+01E9 # LATIN SMALL LETTER K WITH CARON
|
||||
U+0137 # LATIN SMALL LETTER K WITH CEDILLA
|
||||
U+006C # LATIN SMALL LETTER L
|
||||
U+013A # LATIN SMALL LETTER L WITH ACUTE
|
||||
U+013E # LATIN SMALL LETTER L WITH CARON
|
||||
U+013C # LATIN SMALL LETTER L WITH CEDILLA
|
||||
U+0142 # LATIN SMALL LETTER L WITH STROKE
|
||||
U+006D # LATIN SMALL LETTER M
|
||||
U+006E # LATIN SMALL LETTER N
|
||||
U+0148 # LATIN SMALL LETTER N WITH CARON
|
||||
U+00F1 # LATIN SMALL LETTER N WITH TILDE
|
||||
U+0146 # LATIN SMALL LETTER N WITH CEDILLA
|
||||
U+006F # LATIN SMALL LETTER O
|
||||
U+00F2 # LATIN SMALL LETTER O WITH GRAVE
|
||||
U+00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX
|
||||
U+00F6 # LATIN SMALL LETTER O WITH DIAERESIS
|
||||
U+0151 # LATIN SMALL LETTER O WITH DOUBLE ACUTE
|
||||
U+00F5 # LATIN SMALL LETTER O WITH TILDE
|
||||
U+00F8 # LATIN SMALL LETTER O WITH STROKE
|
||||
U+0153 # LATIN SMALL LIGATURE OE
|
||||
U+0070 # LATIN SMALL LETTER P
|
||||
U+0071 # LATIN SMALL LETTER Q
|
||||
U+0072 # LATIN SMALL LETTER R
|
||||
U+0155 # LATIN SMALL LETTER R WITH ACUTE
|
||||
U+0159 # LATIN SMALL LETTER R WITH CARON
|
||||
U+0073 # LATIN SMALL LETTER S
|
||||
U+015B # LATIN SMALL LETTER S WITH ACUTE
|
||||
U+0161 # LATIN SMALL LETTER S WITH CARON
|
||||
U+015F # LATIN SMALL LETTER S WITH CEDILLA
|
||||
U+0074 # LATIN SMALL LETTER T
|
||||
U+0165 # LATIN SMALL LETTER T WITH CARON
|
||||
U+0167 # LATIN SMALL LETTER T WITH STROKE
|
||||
U+0075 # LATIN SMALL LETTER U
|
||||
U+00F9 # LATIN SMALL LETTER U WITH GRAVE
|
||||
U+00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX
|
||||
U+016F # LATIN SMALL LETTER U WITH RING ABOVE
|
||||
U+0171 # LATIN SMALL LETTER U WITH DOUBLE ACUTE
|
||||
U+0173 # LATIN SMALL LETTER U WITH OGONEK
|
||||
U+016B # LATIN SMALL LETTER U WITH MACRON
|
||||
U+0076 # LATIN SMALL LETTER V
|
||||
U+0077 # LATIN SMALL LETTER W
|
||||
U+0175 # LATIN SMALL LETTER W WITH CIRCUMFLEX
|
||||
U+0078 # LATIN SMALL LETTER X
|
||||
U+0079 # LATIN SMALL LETTER Y
|
||||
U+00FD # LATIN SMALL LETTER Y WITH ACUTE
|
||||
U+0177 # LATIN SMALL LETTER Y WITH CIRCUMFLEX
|
||||
U+00FF # LATIN SMALL LETTER Y WITH DIAERESIS
|
||||
U+007A # LATIN SMALL LETTER Z
|
||||
U+017A # LATIN SMALL LETTER Z WITH ACUTE
|
||||
U+017E # LATIN SMALL LETTER Z WITH CARON
|
||||
U+0292 # LATIN SMALL LETTER EZH
|
||||
U+01EF # LATIN SMALL LETTER EZH WITH CARON
|
||||
U+00FE # LATIN SMALL LETTER THORN
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Verify.verifyNotNull;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.UrlConnectionUtils;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Optional;
|
||||
@@ -45,8 +45,11 @@ final class LordnRequestInitializer {
|
||||
/** Returns the username and password for the current TLD to login to the MarksDB server. */
|
||||
private Optional<String> getMarksDbLordnCredentials(String tld) {
|
||||
if (marksdbLordnPassword.isPresent()) {
|
||||
String lordnUsername = verifyNotNull(Registry.get(tld).getLordnUsername(),
|
||||
"lordnUsername is not set for %s.", Registry.get(tld).getTld());
|
||||
String lordnUsername =
|
||||
verifyNotNull(
|
||||
Tld.get(tld).getLordnUsername(),
|
||||
"lordnUsername is not set for %s.",
|
||||
Tld.get(tld).getTld());
|
||||
return Optional.of(String.format("%s:%s", lordnUsername, marksdbLordnPassword.get()));
|
||||
} else {
|
||||
logger.atWarning().log(
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.tmch;
|
||||
|
||||
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;
|
||||
@@ -26,23 +25,13 @@ 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;
|
||||
|
||||
import com.google.api.client.http.HttpMethods;
|
||||
import com.google.appengine.api.taskqueue.LeaseOptions;
|
||||
import com.google.appengine.api.taskqueue.Queue;
|
||||
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.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
@@ -65,9 +54,7 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
@@ -88,9 +75,7 @@ public final class NordnUploadAction implements Runnable {
|
||||
static final String PATH = "/_dr/task/nordnUpload";
|
||||
static final String LORDN_PHASE_PARAM = "lordnPhase";
|
||||
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private static final Duration LEASE_PERIOD = Duration.standardHours(1);
|
||||
|
||||
/**
|
||||
* A unique (enough) id that is outputted in log lines to make it clear which log lines are
|
||||
@@ -192,46 +177,6 @@ public final class NordnUploadAction implements Runnable {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of queue tasks, each containing a row of CSV data, into a single newline-
|
||||
* delimited String.
|
||||
*/
|
||||
static String convertTasksToCsv(List<TaskHandle> tasks, DateTime now, String columns) {
|
||||
// Use a Set for deduping purposes, so we can be idempotent in case tasks happened to be
|
||||
// enqueued multiple times for a given domain create.
|
||||
ImmutableSortedSet.Builder<String> builder =
|
||||
new ImmutableSortedSet.Builder<>(Ordering.natural());
|
||||
for (TaskHandle task : checkNotNull(tasks)) {
|
||||
String payload = new String(task.getPayload(), UTF_8);
|
||||
if (!Strings.isNullOrEmpty(payload)) {
|
||||
builder.add(payload + '\n');
|
||||
}
|
||||
}
|
||||
ImmutableSortedSet<String> csvLines = builder.build();
|
||||
String header = String.format("1,%s,%d\n%s\n", now, csvLines.size(), columns);
|
||||
return header + Joiner.on("").join(csvLines);
|
||||
}
|
||||
|
||||
/** Leases and returns all tasks from the queue with the specified tag tld, in batches. */
|
||||
List<TaskHandle> loadAllTasks(Queue queue, String tld) {
|
||||
ImmutableList.Builder<TaskHandle> allTasks = new ImmutableList.Builder<>();
|
||||
while (true) {
|
||||
List<TaskHandle> tasks =
|
||||
retrier.callWithRetry(
|
||||
() ->
|
||||
queue.leaseTasks(
|
||||
LeaseOptions.Builder.withTag(tld)
|
||||
.leasePeriod(LEASE_PERIOD.getMillis(), TimeUnit.MILLISECONDS)
|
||||
.countLimit(BATCH_SIZE)),
|
||||
TransientFailureException.class,
|
||||
DeadlineExceededException.class);
|
||||
if (tasks.isEmpty()) {
|
||||
return allTasks.build();
|
||||
}
|
||||
allTasks.addAll(tasks);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload LORDN file to MarksDB.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.tools.UpdateOrDeleteAllocationTokensCommand.getTokenKeys;
|
||||
import static google.registry.util.CollectionUtils.findDuplicates;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
@@ -29,14 +30,16 @@ import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.tldconfig.idn.IdnTableEnum;
|
||||
import google.registry.tools.params.OptionalStringParameter;
|
||||
import google.registry.tools.params.TransitionListParameter.BillingCostTransitions;
|
||||
import google.registry.tools.params.TransitionListParameter.TldStateTransitions;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -234,6 +237,24 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
)
|
||||
Integer numDnsPublishShards;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"--dns_a_plus_aaaa_ttl"},
|
||||
description = "The time to live for DNS A and AAAA records (Ex: PT240S)")
|
||||
Duration dnsAPlusAaaaTtl;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"--dns_ns_ttl"},
|
||||
description = "The time to live for DNS NS records (Ex: PT240S)")
|
||||
Duration dnsNsTtl;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"--dns_ds_ttl"},
|
||||
description = "The time to live for DNS DS records (Ex: PT240S)")
|
||||
Duration dnsDsTtl;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--default_tokens",
|
||||
@@ -244,20 +265,29 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
+ " present default tokens.")
|
||||
List<String> defaultTokens;
|
||||
|
||||
/** Returns the existing registry (for update) or null (for creates). */
|
||||
@Nullable
|
||||
abstract Registry getOldRegistry(String tld);
|
||||
@Parameter(
|
||||
names = "--idn_tables",
|
||||
description =
|
||||
"A comma-separated list of the IDN tables to use for this TLD. Specify an empty list to"
|
||||
+ " remove any previously-set tables and to use the default. All elements must be"
|
||||
+ " IdnTableEnum values")
|
||||
List<String> idnTables;
|
||||
|
||||
abstract ImmutableSet<String> getAllowedRegistrants(Registry oldRegistry);
|
||||
/** Returns the existing tld (for update) or null (for creates). */
|
||||
@Nullable
|
||||
abstract Tld getOldTld(String tld);
|
||||
|
||||
abstract ImmutableSet<String> getAllowedNameservers(Registry oldRegistry);
|
||||
abstract ImmutableSet<String> getAllowedRegistrants(Tld oldTld);
|
||||
|
||||
abstract ImmutableSet<String> getReservedLists(Registry oldRegistry);
|
||||
abstract ImmutableSet<String> getAllowedNameservers(Tld oldTld);
|
||||
|
||||
abstract ImmutableSet<String> getReservedLists(Tld oldTld);
|
||||
|
||||
abstract Optional<Map.Entry<DateTime, TldState>> getTldStateTransitionToAdd();
|
||||
|
||||
/** Subclasses can override this to set their own properties. */
|
||||
void setCommandSpecificProperties(@SuppressWarnings("unused") Registry.Builder builder) {}
|
||||
void setCommandSpecificProperties(@SuppressWarnings("unused") Tld.Builder builder) {}
|
||||
|
||||
/** Subclasses can override this to assert that the command can be run in this environment. */
|
||||
void assertAllowedEnvironment() {}
|
||||
@@ -282,14 +312,14 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
checkArgument(
|
||||
!Character.isDigit(tld.charAt(0)),
|
||||
"TLDs cannot begin with a number");
|
||||
Registry oldRegistry = getOldRegistry(tld);
|
||||
Tld oldTld = getOldTld(tld);
|
||||
// TODO(b/26901539): Add a flag to set the pricing engine once we have more than one option.
|
||||
Registry.Builder builder =
|
||||
oldRegistry == null
|
||||
? new Registry.Builder()
|
||||
Tld.Builder builder =
|
||||
oldTld == null
|
||||
? new Tld.Builder()
|
||||
.setTldStr(tld)
|
||||
.setPremiumPricingEngine(StaticPremiumListPricingEngine.NAME)
|
||||
: oldRegistry.asBuilder();
|
||||
: oldTld.asBuilder();
|
||||
|
||||
if (escrow != null) {
|
||||
builder.setEscrowEnabled(escrow);
|
||||
@@ -306,14 +336,16 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
} else if (tldStateTransitionToAdd.isPresent()) {
|
||||
ImmutableSortedMap.Builder<DateTime, TldState> newTldStateTransitions =
|
||||
ImmutableSortedMap.naturalOrder();
|
||||
if (oldRegistry != null) {
|
||||
if (oldTld != null) {
|
||||
checkArgument(
|
||||
oldRegistry.getTldStateTransitions().lastKey().isBefore(
|
||||
tldStateTransitionToAdd.get().getKey()),
|
||||
oldTld
|
||||
.getTldStateTransitions()
|
||||
.lastKey()
|
||||
.isBefore(tldStateTransitionToAdd.get().getKey()),
|
||||
"Cannot add %s at %s when there is a later transition already scheduled",
|
||||
tldStateTransitionToAdd.get().getValue(),
|
||||
tldStateTransitionToAdd.get().getKey());
|
||||
newTldStateTransitions.putAll(oldRegistry.getTldStateTransitions());
|
||||
newTldStateTransitions.putAll(oldTld.getTldStateTransitions());
|
||||
}
|
||||
builder.setTldStateTransitions(
|
||||
newTldStateTransitions.put(getTldStateTransitionToAdd().get()).build());
|
||||
@@ -352,6 +384,9 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
Optional.ofNullable(lordnUsername).ifPresent(u -> builder.setLordnUsername(u.orElse(null)));
|
||||
Optional.ofNullable(claimsPeriodEnd).ifPresent(builder::setClaimsPeriodEnd);
|
||||
Optional.ofNullable(numDnsPublishShards).ifPresent(builder::setNumDnsPublishLocks);
|
||||
Optional.ofNullable(dnsAPlusAaaaTtl).ifPresent(builder::setDnsAPlusAaaaTtl);
|
||||
Optional.ofNullable(dnsNsTtl).ifPresent(builder::setDnsNsTtl);
|
||||
Optional.ofNullable(dnsDsTtl).ifPresent(builder::setDnsDsTtl);
|
||||
|
||||
if (premiumListName != null) {
|
||||
if (premiumListName.isPresent()) {
|
||||
@@ -377,13 +412,13 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
builder.setDnsWriters(dnsWritersSet);
|
||||
}
|
||||
|
||||
ImmutableSet<String> newReservedListNames = getReservedLists(oldRegistry);
|
||||
ImmutableSet<String> newReservedListNames = getReservedLists(oldTld);
|
||||
checkReservedListValidityForTld(tld, newReservedListNames);
|
||||
builder.setReservedListsByName(newReservedListNames);
|
||||
|
||||
builder.setAllowedRegistrantContactIds(getAllowedRegistrants(oldRegistry));
|
||||
builder.setAllowedRegistrantContactIds(getAllowedRegistrants(oldTld));
|
||||
|
||||
builder.setAllowedFullyQualifiedHostNames(getAllowedNameservers(oldRegistry));
|
||||
builder.setAllowedFullyQualifiedHostNames(getAllowedNameservers(oldTld));
|
||||
|
||||
if (!isNullOrEmpty(defaultTokens)) {
|
||||
if (defaultTokens.equals(ImmutableList.of(""))) {
|
||||
@@ -392,9 +427,26 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
builder.setDefaultPromoTokens(getTokenKeys(defaultTokens, null));
|
||||
}
|
||||
}
|
||||
if (idnTables != null) {
|
||||
if (idnTables.equals(ImmutableList.of(""))) {
|
||||
builder.setIdnTables(ImmutableSet.of());
|
||||
} else {
|
||||
ImmutableSet<String> upperCaseIdnTables =
|
||||
idnTables.stream().map(String::toUpperCase).collect(toImmutableSet());
|
||||
ImmutableSet<String> validIdnStringValues =
|
||||
Arrays.stream(IdnTableEnum.values()).map(Enum::name).collect(toImmutableSet());
|
||||
checkArgument(
|
||||
validIdnStringValues.containsAll(upperCaseIdnTables),
|
||||
"IDN tables %s contained invalid value(s). Possible values: %s",
|
||||
upperCaseIdnTables,
|
||||
validIdnStringValues);
|
||||
builder.setIdnTables(
|
||||
upperCaseIdnTables.stream().map(IdnTableEnum::valueOf).collect(toImmutableSet()));
|
||||
}
|
||||
}
|
||||
// Update the Registry object.
|
||||
setCommandSpecificProperties(builder);
|
||||
stageEntityChange(oldRegistry, builder.build());
|
||||
stageEntityChange(oldTld, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -68,40 +68,39 @@ class CreateTldCommand extends CreateOrUpdateTldCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
void setCommandSpecificProperties(Registry.Builder builder) {
|
||||
void setCommandSpecificProperties(Tld.Builder builder) {
|
||||
// Pick up the currency from the create cost. Since all costs must be in one currency, and that
|
||||
// condition is enforced by the builder, it doesn't matter which cost we choose it from.
|
||||
CurrencyUnit currency = createBillingCost != null
|
||||
? createBillingCost.getCurrencyUnit()
|
||||
: Registry.DEFAULT_CURRENCY;
|
||||
CurrencyUnit currency =
|
||||
createBillingCost != null ? createBillingCost.getCurrencyUnit() : Tld.DEFAULT_CURRENCY;
|
||||
|
||||
builder.setCurrency(currency);
|
||||
|
||||
// If this is a non-default currency and the user hasn't specified an EAP fee schedule, set the
|
||||
// EAP fee schedule to a matching currency.
|
||||
if (!currency.equals(Registry.DEFAULT_CURRENCY) && eapFeeSchedule.isEmpty()) {
|
||||
if (!currency.equals(Tld.DEFAULT_CURRENCY) && eapFeeSchedule.isEmpty()) {
|
||||
builder.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(currency)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Registry getOldRegistry(String tld) {
|
||||
Tld getOldTld(String tld) {
|
||||
checkState(!getTlds().contains(tld), "TLD '%s' already exists", tld);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableSet<String> getAllowedRegistrants(Registry oldRegistry) {
|
||||
ImmutableSet<String> getAllowedRegistrants(Tld oldTld) {
|
||||
return ImmutableSet.copyOf(nullToEmpty(allowedRegistrants));
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableSet<String> getAllowedNameservers(Registry oldRegistry) {
|
||||
ImmutableSet<String> getAllowedNameservers(Tld oldTld) {
|
||||
return ImmutableSet.copyOf(nullToEmpty(allowedNameservers));
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableSet<String> getReservedLists(Registry oldRegistry) {
|
||||
ImmutableSet<String> getReservedLists(Tld oldTld) {
|
||||
return ImmutableSet.copyOf(nullToEmpty(reservedListNames));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,25 +21,25 @@ import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
|
||||
/**
|
||||
* Command to delete the {@link Registry} associated with the specified TLD in the database.
|
||||
* Command to delete the {@link Tld} associated with the specified TLD in the database.
|
||||
*
|
||||
* <p>This command will fail if any domains are currently registered on the TLD.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Delete a TLD from the database.")
|
||||
final class DeleteTldCommand extends ConfirmingCommand {
|
||||
|
||||
private Registry registry;
|
||||
private Tld tld;
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "The TLD to delete.",
|
||||
required = true)
|
||||
private String tld;
|
||||
private String tldStr;
|
||||
|
||||
/**
|
||||
* Perform the command by deleting the TLD.
|
||||
@@ -52,28 +52,28 @@ final class DeleteTldCommand extends ConfirmingCommand {
|
||||
*/
|
||||
@Override
|
||||
protected void init() {
|
||||
registry = Registry.get(tld);
|
||||
checkState(registry.getTldType().equals(TldType.TEST), "Cannot delete a real TLD");
|
||||
tld = Tld.get(tldStr);
|
||||
checkState(tld.getTldType().equals(TldType.TEST), "Cannot delete a real TLD");
|
||||
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
checkState(
|
||||
!registrar.getAllowedTlds().contains(tld),
|
||||
!registrar.getAllowedTlds().contains(tldStr),
|
||||
"Cannot delete TLD because registrar %s lists it as an allowed TLD",
|
||||
registrar.getRegistrarId());
|
||||
}
|
||||
checkState(!tldContainsDomains(tld), "Cannot delete TLD because a domain is defined on it");
|
||||
checkState(!tldContainsDomains(tldStr), "Cannot delete TLD because a domain is defined on it");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return "You are about to delete TLD: " + tld;
|
||||
return "You are about to delete TLD: " + tldStr;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() {
|
||||
tm().transact(() -> tm().delete(registry));
|
||||
registry.invalidateInCache();
|
||||
return String.format("Deleted TLD '%s'.\n", tld);
|
||||
tm().transact(() -> tm().delete(tld));
|
||||
tld.invalidateInCache();
|
||||
return String.format("Deleted TLD '%s'.\n", tldStr);
|
||||
}
|
||||
|
||||
private boolean tldContainsDomains(String tld) {
|
||||
|
||||
@@ -32,8 +32,8 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.RegistryLockDao;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.util.Optional;
|
||||
@@ -402,7 +402,7 @@ public final class DomainLockUtils {
|
||||
.setReason(Reason.SERVER_STATUS)
|
||||
.setTargetId(domain.getForeignKey())
|
||||
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
|
||||
.setCost(Registry.get(domain.getTld()).getRegistryLockOrUnlockBillingCost())
|
||||
.setCost(Tld.get(domain.getTld()).getRegistryLockOrUnlockBillingCost())
|
||||
.setEventTime(now)
|
||||
.setBillingTime(now)
|
||||
.setDomainHistory(domainHistory)
|
||||
|
||||
@@ -102,7 +102,9 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--type"},
|
||||
description = "Type of type token, either SINGLE_USE (default) or UNLIMITED_USE")
|
||||
description =
|
||||
"Type of type token, either DEFAULT_PROMO, PACKAGE, SINGLE_USE (default) or"
|
||||
+ " UNLIMITED_USE")
|
||||
private TokenType tokenType;
|
||||
|
||||
@Parameter(
|
||||
|
||||
@@ -18,7 +18,7 @@ import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a TLD record. */
|
||||
@@ -33,7 +33,7 @@ final class GetTldCommand implements Command {
|
||||
@Override
|
||||
public void run() {
|
||||
for (String tld : assertTldsExist(mainParameters)) {
|
||||
System.out.println(Registry.get(tld));
|
||||
System.out.println(Tld.get(tld));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -54,7 +54,7 @@ final class ListCursorsCommand implements Command {
|
||||
Map<String, VKey<Cursor>> cursorKeys =
|
||||
cursorType.isScoped()
|
||||
? Registries.getTlds().stream()
|
||||
.map(Registry::get)
|
||||
.map(Tld::get)
|
||||
.filter(r -> r.getTldType() == filterTldType)
|
||||
.filter(r -> !filterEscrowEnabled || r.getEscrowEnabled())
|
||||
.collect(
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.tools.server.ListDomainsAction;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ public final class RegistryTool {
|
||||
.put("update_keyring_secret", UpdateKeyringSecretCommand.class)
|
||||
.put("update_package_promotion", UpdatePackagePromotionCommand.class)
|
||||
.put("update_premium_list", UpdatePremiumListCommand.class)
|
||||
.put("update_recurrence", UpdateRecurrenceCommand.class)
|
||||
.put("update_registrar", UpdateRegistrarCommand.class)
|
||||
.put("update_reserved_list", UpdateReservedListCommand.class)
|
||||
.put("update_server_locks", UpdateServerLocksCommand.class)
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.common.Cursor;
|
||||
import google.registry.model.common.Cursor.CursorType;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.tools.params.DateTimeParameter;
|
||||
import java.util.List;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -52,9 +52,9 @@ final class UpdateCursorsCommand extends ConfirmingCommand implements Command {
|
||||
if (isNullOrEmpty(tlds)) {
|
||||
result.add(Cursor.createGlobal(cursorType, newTimestamp));
|
||||
} else {
|
||||
for (String tld : tlds) {
|
||||
Registry registry = Registry.get(tld);
|
||||
result.add(Cursor.createScoped(cursorType, newTimestamp, registry));
|
||||
for (String tldStr : tlds) {
|
||||
Tld tld = Tld.get(tldStr);
|
||||
result.add(Cursor.createScoped(cursorType, newTimestamp, tld));
|
||||
}
|
||||
}
|
||||
cursorsToUpdate = result.build();
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
// 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 static google.registry.model.IdService.allocateId;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.billing.BillingEvent.Recurring;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Command to update {@link Recurring} billing events with a new behavior and/or price.
|
||||
*
|
||||
* <p>More specifically, this closes the existing recurrence object and creates a new, similar,
|
||||
* object as well as a corresponding synthetic {@link DomainHistory} object. This is done to
|
||||
* preserve the recurrence's history.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Update a billing recurrence")
|
||||
public class UpdateRecurrenceCommand extends ConfirmingCommand {
|
||||
|
||||
private static final String HISTORY_REASON =
|
||||
"Administrative update of billing recurrence behavior";
|
||||
|
||||
@Parameter(
|
||||
description = "Domain name(s) for which we wish to update the recurrence(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--renewal_price_behavior",
|
||||
description = "New RenewalPriceBehavior value to use with this recurrence")
|
||||
RenewalPriceBehavior renewalPriceBehavior;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--specified_renewal_price",
|
||||
description = "Exact renewal price to use if the behavior is SPECIFIED")
|
||||
Money specifiedRenewalPrice;
|
||||
|
||||
@Override
|
||||
protected String prompt() throws Exception {
|
||||
checkArgument(
|
||||
renewalPriceBehavior != null || specifiedRenewalPrice != null,
|
||||
"Must specify a behavior and/or a price");
|
||||
|
||||
if (renewalPriceBehavior != null) {
|
||||
if (renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED)) {
|
||||
checkArgument(
|
||||
specifiedRenewalPrice != null,
|
||||
"Renewal price must be set when using SPECIFIED behavior");
|
||||
} else {
|
||||
checkArgument(
|
||||
specifiedRenewalPrice == null,
|
||||
"Renewal price can have a value if and only if the renewal price behavior is"
|
||||
+ " SPECIFIED");
|
||||
}
|
||||
}
|
||||
ImmutableMap<Domain, Recurring> domainsAndRecurrings =
|
||||
tm().transact(this::loadDomainsAndRecurrings);
|
||||
if (renewalPriceBehavior == null) {
|
||||
// Allow users to specify only a price only if all renewals are already SPECIFIED
|
||||
domainsAndRecurrings.forEach(
|
||||
(d, r) ->
|
||||
checkArgument(
|
||||
r.getRenewalPriceBehavior().equals(RenewalPriceBehavior.SPECIFIED),
|
||||
"When specifying only a price, all domains must have SPECIFIED behavior. Domain"
|
||||
+ " %s does not",
|
||||
d.getDomainName()));
|
||||
}
|
||||
String specifiedPriceString =
|
||||
specifiedRenewalPrice == null ? "" : String.format(" and price %s", specifiedRenewalPrice);
|
||||
return String.format(
|
||||
"Update the following with behavior %s%s?\n%s",
|
||||
renewalPriceBehavior,
|
||||
specifiedPriceString,
|
||||
Joiner.on('\n').withKeyValueSeparator(':').join(domainsAndRecurrings));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
ImmutableList<Recurring> newRecurrings = tm().transact(this::internalExecute);
|
||||
return "Updated new recurring(s): " + newRecurrings;
|
||||
}
|
||||
|
||||
private ImmutableList<Recurring> internalExecute() {
|
||||
ImmutableMap<Domain, Recurring> domainsAndRecurrings = loadDomainsAndRecurrings();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
ImmutableList.Builder<Recurring> resultBuilder = new ImmutableList.Builder<>();
|
||||
domainsAndRecurrings.forEach(
|
||||
(domain, existingRecurring) -> {
|
||||
// Make a new history ID to break the (recurring, history, domain) circular dep chain
|
||||
long newHistoryId = allocateId();
|
||||
HistoryEntryId newDomainHistoryId = new HistoryEntryId(domain.getRepoId(), newHistoryId);
|
||||
Recurring endingNow = existingRecurring.asBuilder().setRecurrenceEndTime(now).build();
|
||||
Recurring.Builder newRecurringBuilder =
|
||||
existingRecurring
|
||||
.asBuilder()
|
||||
// set the ID to be 0 (null) to create a new object
|
||||
.setId(0)
|
||||
.setDomainHistoryId(newDomainHistoryId);
|
||||
if (renewalPriceBehavior != null) {
|
||||
newRecurringBuilder.setRenewalPriceBehavior(renewalPriceBehavior);
|
||||
newRecurringBuilder.setRenewalPrice(null);
|
||||
}
|
||||
if (specifiedRenewalPrice != null) {
|
||||
newRecurringBuilder.setRenewalPrice(specifiedRenewalPrice);
|
||||
}
|
||||
Recurring newRecurring = newRecurringBuilder.build();
|
||||
Domain newDomain =
|
||||
domain.asBuilder().setAutorenewBillingEvent(newRecurring.createVKey()).build();
|
||||
DomainHistory newDomainHistory =
|
||||
new DomainHistory.Builder()
|
||||
.setRevisionId(newDomainHistoryId.getRevisionId())
|
||||
.setDomain(newDomain)
|
||||
.setReason(HISTORY_REASON)
|
||||
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
|
||||
.setBySuperuser(true)
|
||||
.setRequestedByRegistrar(false)
|
||||
.setType(HistoryEntry.Type.SYNTHETIC)
|
||||
.setModificationTime(now)
|
||||
.build();
|
||||
tm().putAll(endingNow, newRecurring, newDomain, newDomainHistory);
|
||||
resultBuilder.add(newRecurring);
|
||||
});
|
||||
return resultBuilder.build();
|
||||
}
|
||||
|
||||
private ImmutableMap<Domain, Recurring> loadDomainsAndRecurrings() {
|
||||
ImmutableMap.Builder<Domain, Recurring> result = new ImmutableMap.Builder<>();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
for (String domainName : mainParameters) {
|
||||
Domain domain =
|
||||
EppResourceUtils.loadByForeignKey(Domain.class, domainName, now)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format(
|
||||
"Domain %s does not exist or has been deleted", domainName)));
|
||||
checkArgument(
|
||||
domain.getDeletionTime().equals(END_OF_TIME),
|
||||
"Domain %s has already had a deletion time set",
|
||||
domainName);
|
||||
checkArgument(
|
||||
domain.getTransferData().isEmpty(),
|
||||
"Domain %s has a pending transfer: %s",
|
||||
domainName,
|
||||
domain.getTransferData());
|
||||
Optional<DateTime> domainAutorenewEndTime = domain.getAutorenewEndTime();
|
||||
domainAutorenewEndTime.ifPresent(
|
||||
endTime ->
|
||||
checkArgument(
|
||||
endTime.isAfter(now),
|
||||
"Domain %s autorenew ended prior to now at %s",
|
||||
domainName,
|
||||
endTime));
|
||||
Recurring recurring = tm().loadByKey(domain.getAutorenewBillingEvent());
|
||||
checkArgument(
|
||||
recurring.getRecurrenceEndTime().equals(END_OF_TIME),
|
||||
"Domain %s's recurrence's end date is not END_OF_TIME",
|
||||
domainName);
|
||||
result.put(domain, recurring);
|
||||
}
|
||||
return result.build();
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,8 @@ import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -83,35 +83,35 @@ public class UpdateTldCommand extends CreateOrUpdateTldCommand {
|
||||
TldState setCurrentTldState;
|
||||
|
||||
@Override
|
||||
Registry getOldRegistry(String tld) {
|
||||
return Registry.get(assertTldExists(tld));
|
||||
Tld getOldTld(String tld) {
|
||||
return Tld.get(assertTldExists(tld));
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableSet<String> getAllowedRegistrants(Registry oldRegistry) {
|
||||
ImmutableSet<String> getAllowedRegistrants(Tld oldTld) {
|
||||
return formUpdatedList(
|
||||
"allowed registrants",
|
||||
oldRegistry.getAllowedRegistrantContactIds(),
|
||||
oldTld.getAllowedRegistrantContactIds(),
|
||||
allowedRegistrants,
|
||||
allowedRegistrantsAdd,
|
||||
allowedRegistrantsRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableSet<String> getAllowedNameservers(Registry oldRegistry) {
|
||||
ImmutableSet<String> getAllowedNameservers(Tld oldTld) {
|
||||
return formUpdatedList(
|
||||
"allowed nameservers",
|
||||
oldRegistry.getAllowedFullyQualifiedHostNames(),
|
||||
oldTld.getAllowedFullyQualifiedHostNames(),
|
||||
allowedNameservers,
|
||||
allowedNameserversAdd,
|
||||
allowedNameserversRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableSet<String> getReservedLists(Registry oldRegistry) {
|
||||
ImmutableSet<String> getReservedLists(Tld oldTld) {
|
||||
return formUpdatedList(
|
||||
"reserved lists",
|
||||
oldRegistry.getReservedListNames(),
|
||||
oldTld.getReservedListNames(),
|
||||
reservedListNames,
|
||||
reservedListsAdd,
|
||||
reservedListsRemove);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools.params;
|
||||
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
|
||||
/**
|
||||
* {@link TldState} CLI parameter converter/validator. Required to support multi-value
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import static google.registry.request.Action.Method.POST;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
@@ -35,7 +35,7 @@ import org.joda.time.DateTime;
|
||||
path = ListTldsAction.PATH,
|
||||
method = {GET, POST},
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public final class ListTldsAction extends ListObjectsAction<Registry> {
|
||||
public final class ListTldsAction extends ListObjectsAction<Tld> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/tlds";
|
||||
|
||||
@@ -48,8 +48,8 @@ public final class ListTldsAction extends ListObjectsAction<Registry> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<Registry> loadObjects() {
|
||||
return getTlds().stream().map(Registry::get).collect(toImmutableSet());
|
||||
public ImmutableSet<Tld> loadObjects() {
|
||||
return getTlds().stream().map(Tld::get).collect(toImmutableSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,15 +61,15 @@ public final class ListTldsAction extends ListObjectsAction<Registry> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableMap<String, String> getFieldOverrides(Registry registry) {
|
||||
public ImmutableMap<String, String> getFieldOverrides(Tld tld) {
|
||||
final DateTime now = clock.nowUtc();
|
||||
return new ImmutableMap.Builder<String, String>()
|
||||
.put("dnsPaused", registry.getDnsPaused() ? "paused" : "-")
|
||||
.put("escrowEnabled", registry.getEscrowEnabled() ? "enabled" : "-")
|
||||
.put("tldState", registry.isPdt(now) ? "PDT" : registry.getTldState(now).toString())
|
||||
.put("tldStateTransitions", registry.getTldStateTransitions().toString())
|
||||
.put("renewBillingCost", registry.getStandardRenewCost(now).toString())
|
||||
.put("renewBillingCostTransitions", registry.getRenewBillingCostTransitions().toString())
|
||||
.put("dnsPaused", tld.getDnsPaused() ? "paused" : "-")
|
||||
.put("escrowEnabled", tld.getEscrowEnabled() ? "enabled" : "-")
|
||||
.put("tldState", tld.isPdt(now) ? "PDT" : tld.getTldState(now).toString())
|
||||
.put("tldStateTransitions", tld.getTldStateTransitions().toString())
|
||||
.put("renewBillingCost", tld.getStandardRenewCost(now).toString())
|
||||
.put("renewBillingCostTransitions", tld.getRenewBillingCostTransitions().toString())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<class>google.registry.model.tld.label.PremiumList$PremiumEntry</class>
|
||||
<class>google.registry.model.tld.label.ReservedList</class>
|
||||
<class>google.registry.model.tld.label.ReservedList$ReservedListEntry</class>
|
||||
<class>google.registry.model.tld.Registry</class>
|
||||
<class>google.registry.model.tld.Tld</class>
|
||||
<class>google.registry.model.reporting.DomainTransactionRecord</class>
|
||||
<class>google.registry.model.reporting.Spec11ThreatMatch</class>
|
||||
<class>google.registry.model.server.Lock</class>
|
||||
@@ -91,6 +91,7 @@
|
||||
<class>google.registry.persistence.converter.DatabaseMigrationScheduleTransitionConverter</class>
|
||||
<class>google.registry.persistence.converter.DateTimeConverter</class>
|
||||
<class>google.registry.persistence.converter.DurationConverter</class>
|
||||
<class>google.registry.persistence.converter.IdnTableEnumSetConverter</class>
|
||||
<class>google.registry.persistence.converter.InetAddressSetConverter</class>
|
||||
<class>google.registry.persistence.converter.LocalDateConverter</class>
|
||||
<class>google.registry.persistence.converter.PostalInfoChoiceListConverter</class>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user