mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b78d12e73f | |||
| 4a1d0609f3 | |||
| 61b121f464 | |||
| 074f78cfb3 | |||
| 7be5fe4c01 | |||
| 1876e2c3e8 | |||
| 49f14b5e1b |
@@ -0,0 +1,52 @@
|
||||
# Engineering Standards for Gemini CLI
|
||||
|
||||
This document outlines foundational mandates, architectural patterns, and project-specific conventions to ensure high-quality, idiomatic, and consistent code from the first iteration.
|
||||
|
||||
## Core Mandates
|
||||
|
||||
### 1. Rigorous Import Management
|
||||
- **Addition:** When adding new symbols, ensure the corresponding import is added.
|
||||
- **Removal:** When removing the last usage of a class or symbol from a file (e.g., removing a `@Inject Clock clock;` field), **immediately remove the associated import**. Do not wait for a build failure to identify unused imports.
|
||||
- **Checkstyle:** Proactively fix common checkstyle errors (line length > 100, formatting, unused imports) during the initial code write. Do not wait for CI/build failures to address these, as iterative fixes are inefficient.
|
||||
- **Verification:** Before finalizing any change, scan the imports section for redundancy.
|
||||
|
||||
### 2. Time and Precision Handling (java.time Migration)
|
||||
- **Millisecond Precision:** Always truncate `Instant.now()` to milliseconds (using `.truncatedTo(ChronoUnit.MILLIS)`) to maintain consistency with Joda `DateTime` and the PostgreSQL schema (which enforces millisecond precision via JPA converters).
|
||||
- **Clock Injection:**
|
||||
- Avoid direct calls to `Instant.now()`, `DateTime.now()`, `ZonedDateTime.now()`, or `System.currentTimeMillis()`.
|
||||
- Inject `google.registry.util.Clock` (production) or `google.registry.testing.FakeClock` (tests).
|
||||
- Use `clock.nowDate()` to get a `ZonedDateTime` in UTC.
|
||||
- **Beam Pipelines:**
|
||||
- Ensure `Clock` is serializable (it is by default in this project) when used in Beam `DoFn`s.
|
||||
- Pass the `Clock` through the constructor or via Dagger provider methods in the pipeline module.
|
||||
- **Command-Line Tools:**
|
||||
- Use `@Inject Clock clock;` in `Command` implementations.
|
||||
- The `clock` field should be **package-private** (no access modifier) to allow manual initialization in corresponding test classes.
|
||||
- In test classes (e.g., `UpdateDomainCommandTest`), manually set `command.clock = fakeClock;` in the `@BeforeEach` method.
|
||||
- Base test classes like `EppToolCommandTestCase` should handle this assignment for their generic command types where applicable.
|
||||
|
||||
### 3. Dependency Injection (Dagger)
|
||||
- **Concrete Types:** Dagger `inject` methods must use explicit concrete types. Generic `inject(Command)` methods will not work.
|
||||
- **Test Components:** Use `TestRegistryToolComponent` for command-line tool tests to bridge the gap between `main` and `nonprod/test` source sets.
|
||||
|
||||
### 4. Database Consistency
|
||||
- **JPA Converters:** Be aware that JPA converters (like `DateTimeConverter`) may perform truncation or transformation. Ensure application-level logic matches these transformations to avoid "dirty" state or unexpected diffs.
|
||||
- **Transaction Management:**
|
||||
- **Top-Level:** Define database transactions (`tm().transact(...)`) at the highest possible level in the call chain (e.g., in an Action, a Command, or a Flow). This ensures all operations are atomic and handled by the retry logic.
|
||||
- **DAO Methods:** Avoid declaring transactions inside low-level DAO methods. Use `tm().assertInTransaction()` to ensure that these methods are only called within a valid transactional context.
|
||||
- **Utility/Cache Methods:** Use `tm().reTransact(...)` for utility methods or Caffeine cache loaders that might be invoked from both transactional and non-transactional paths.
|
||||
- `reTransact` will join an existing transaction if one is present (acting as a no-op) or start a new one if not.
|
||||
- This is particularly useful for in-memory caches where the loader must be able to fetch data regardless of whether the caller is currently in a transaction.
|
||||
- **Transactional Time:** Ensure code that relies on `tm().getTransactionTime()` is executed within a transaction context.
|
||||
|
||||
### 5. Testing Best Practices
|
||||
- **FakeClock and Sleeper:** Use `FakeClock` and `Sleeper` for any logic involving timeouts, delays, or expiration.
|
||||
- **Empirical Reproduction:** Before fixing a bug, always create a test case that reproduces the failure.
|
||||
- **Base Classes:** Leverage `CommandTestCase`, `EppToolCommandTestCase`, etc., to reduce boilerplate and ensure consistent setup (e.g., clock initialization).
|
||||
|
||||
### 6. Project Dependencies
|
||||
- **Common Module:** When using `Clock` or other core utilities in a new or separate module (like `load-testing`), ensure `implementation project(':common')` is added to the module's `build.gradle`.
|
||||
|
||||
## Performance and Efficiency
|
||||
- **Turn Minimization:** Aim for "perfect" code in the first iteration. Iterative fixes for checkstyle or compilation errors consume significant context and time.
|
||||
- **Context Management:** Use sub-agents for batch refactoring or high-volume output tasks to keep the main session history lean and efficient.
|
||||
@@ -200,6 +200,7 @@ allprojects {
|
||||
"--add-exports",
|
||||
"jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"]
|
||||
options.forkOptions.jvmArgs = ["-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
|
||||
"-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
|
||||
|
||||
@@ -16,6 +16,8 @@ package google.registry.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -36,4 +38,9 @@ public interface Clock extends Serializable {
|
||||
|
||||
/** Returns current Instant (which is always in UTC). */
|
||||
Instant now();
|
||||
|
||||
/** Returns the current time as a {@link ZonedDateTime} in UTC. */
|
||||
default ZonedDateTime nowDate() {
|
||||
return ZonedDateTime.ofInstant(now(), ZoneOffset.UTC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +170,12 @@ public abstract class DateTimeUtils {
|
||||
return (instant == null) ? null : new DateTime(instant.toEpochMilli(), DateTimeZone.UTC);
|
||||
}
|
||||
|
||||
/** Convert a java.time {@link java.time.Instant} to a joda {@link org.joda.time.Instant}. */
|
||||
@Nullable
|
||||
public static org.joda.time.Instant toJodaInstant(@Nullable java.time.Instant instant) {
|
||||
return (instant == null) ? null : org.joda.time.Instant.ofEpochMilli(instant.toEpochMilli());
|
||||
}
|
||||
|
||||
public static Instant plusYears(Instant instant, int years) {
|
||||
return instant.atZone(ZoneOffset.UTC).plusYears(years).toInstant();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ public class SystemClock implements Clock {
|
||||
|
||||
@Override
|
||||
public Instant now() {
|
||||
return Instant.now();
|
||||
// Truncate to milliseconds to match the precision of Joda DateTime and our database schema
|
||||
// (which uses millisecond precision via DateTimeConverter). This prevents subtle comparison
|
||||
// bugs where a high-precision Instant would be considered "after" a truncated database
|
||||
// timestamp.
|
||||
return Instant.now().truncatedTo(java.time.temporal.ChronoUnit.MILLIS);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -74,11 +74,7 @@ sourceSets {
|
||||
nonprod {
|
||||
java {
|
||||
compileClasspath += main.output
|
||||
|
||||
// Add the DB runtime classpath to nonprod so we can load the flyway
|
||||
// scripts.
|
||||
runtimeClasspath += main.output +
|
||||
rootProject.project(":db").sourceSets.main.runtimeClasspath
|
||||
runtimeClasspath += main.output
|
||||
}
|
||||
}
|
||||
test {
|
||||
@@ -102,6 +98,7 @@ configurations {
|
||||
devtool
|
||||
|
||||
nonprodImplementation.extendsFrom implementation
|
||||
nonprodRuntimeOnly.extendsFrom runtimeOnly
|
||||
|
||||
testImplementation.extendsFrom nonprodImplementation
|
||||
|
||||
@@ -259,6 +256,7 @@ dependencies {
|
||||
implementation project(':util')
|
||||
// Import NomulusPostreSql from ':db' for implementation but exclude dependencies.
|
||||
implementation project(path: ':db', configuration: 'implementationApi')
|
||||
nonprodRuntimeOnly project(':db')
|
||||
testRuntimeOnly project(':db')
|
||||
|
||||
annotationProcessor deps['com.google.auto.service:auto-service']
|
||||
|
||||
+15
-8
@@ -100,8 +100,10 @@ com.google.apis:google-api-services-admin-directory:directory_v1-rev20260227-2.0
|
||||
com.google.apis:google-api-services-bigquery:v2-rev20240815-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20240310-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dataflow:v1b3-rev20260213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20260219-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-drive:v3-rev20260322-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20260219-2.0.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20260402-2.0.0=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
com.google.apis:google-api-services-drive:v3-rev20260322-2.0.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-drive:v3-rev20260405-2.0.0=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
com.google.apis:google-api-services-gmail:v1-rev20260112-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -132,7 +134,7 @@ com.google.cloud.opentelemetry:detector-resources-support:0.33.0=deploy_jar,nonp
|
||||
com.google.cloud.opentelemetry:exporter-metrics:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.28.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.28.2=deploy_jar,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.28.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigquerystorage:3.9.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-bigquerystorage:3.9.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigtable:2.43.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
@@ -601,12 +603,17 @@ tools.jackson.core:jackson-core:3.1.0=compileClasspath,deploy_jar,nonprodCompile
|
||||
tools.jackson.core:jackson-databind:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
tools.jackson:jackson-bom:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-api:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-diagram:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-operations:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-postgresql:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-text:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-diagram:17.10.0=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
us.fatehi:schemacrawler-diagram:17.9.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-operations:17.10.0=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
us.fatehi:schemacrawler-operations:17.9.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-postgresql:17.10.0=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
us.fatehi:schemacrawler-postgresql:17.9.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-text:17.10.0=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
us.fatehi:schemacrawler-text:17.9.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-tools:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-utility:17.1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler:17.9.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler:17.10.0=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath
|
||||
us.fatehi:schemacrawler:17.9.0=deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
xerces:xmlParserAPIs:2.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
empty=devtool,shadow
|
||||
|
||||
@@ -28,7 +28,6 @@ import static google.registry.request.RequestParameters.PARAM_BATCH_SIZE;
|
||||
import static google.registry.request.RequestParameters.PARAM_DRY_RUN;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
import static google.registry.util.RegistryEnvironment.PRODUCTION;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -44,6 +43,7 @@ import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
@@ -111,16 +111,20 @@ public class DeleteProberDataAction implements Runnable {
|
||||
|
||||
String registryAdminRegistrarId;
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
DeleteProberDataAction(
|
||||
@Parameter(PARAM_DRY_RUN) boolean isDryRun,
|
||||
@Parameter(PARAM_TLDS) ImmutableSet<String> tlds,
|
||||
@Parameter(PARAM_BATCH_SIZE) Optional<Integer> batchSize,
|
||||
@Config("registryAdminClientId") String registryAdminRegistrarId) {
|
||||
@Config("registryAdminClientId") String registryAdminRegistrarId,
|
||||
Clock clock) {
|
||||
this.isDryRun = isDryRun;
|
||||
this.tlds = tlds;
|
||||
this.batchSize = batchSize.orElse(DEFAULT_BATCH_SIZE);
|
||||
this.registryAdminRegistrarId = registryAdminRegistrarId;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,7 +149,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
AtomicInteger softDeletedDomains = new AtomicInteger();
|
||||
AtomicInteger hardDeletedDomains = new AtomicInteger();
|
||||
AtomicReference<ImmutableList<Domain>> domainsBatch = new AtomicReference<>();
|
||||
DateTime startTime = DateTime.now(UTC);
|
||||
DateTime startTime = clock.nowUtc();
|
||||
do {
|
||||
tm().transact(
|
||||
TRANSACTION_REPEATABLE_READ,
|
||||
@@ -164,7 +168,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
hardDeletedDomains.get(), batchSize);
|
||||
|
||||
// Automatically kill the job if it is running for over 20 hours
|
||||
} while (DateTime.now(UTC).isBefore(startTime.plusHours(20))
|
||||
} while (clock.nowUtc().isBefore(startTime.plusHours(20))
|
||||
&& domainsBatch.get().size() == batchSize);
|
||||
logger.atInfo().log(
|
||||
"%s %d domains.",
|
||||
|
||||
+6
-3
@@ -19,7 +19,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -37,6 +36,7 @@ import google.registry.model.registrar.RegistrarPoc.Type;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
@@ -73,6 +73,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
private final GmailClient gmailClient;
|
||||
private final String expirationWarningEmailSubjectText;
|
||||
private final Response response;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
public SendExpiringCertificateNotificationEmailAction(
|
||||
@@ -80,12 +81,14 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
@Config("expirationWarningEmailSubjectText") String expirationWarningEmailSubjectText,
|
||||
GmailClient gmailClient,
|
||||
CertificateChecker certificateChecker,
|
||||
Response response) {
|
||||
Response response,
|
||||
Clock clock) {
|
||||
this.certificateChecker = certificateChecker;
|
||||
this.expirationWarningEmailSubjectText = expirationWarningEmailSubjectText;
|
||||
this.gmailClient = gmailClient;
|
||||
this.expirationWarningEmailBodyText = expirationWarningEmailBodyText;
|
||||
this.response = response;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -186,7 +189,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
*/
|
||||
updateLastNotificationSentDate(
|
||||
registrar,
|
||||
DateTime.now(UTC).minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()),
|
||||
clock.nowUtc().minusMinutes((int) UPDATE_TIME_OFFSET.getStandardMinutes()),
|
||||
certificateType);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.CharStreams;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -40,7 +42,6 @@ import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.protocol.HTTP;
|
||||
import org.joda.time.Instant;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -74,6 +75,8 @@ public class SafeBrowsingTransforms {
|
||||
/** Provides the SafeBrowsing API key at runtime. */
|
||||
private final String apiKey;
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
/**
|
||||
* Maps a domain name's {@code domainName} to its corresponding {@link DomainNameInfo} to
|
||||
* facilitate batching SafeBrowsing API requests.
|
||||
@@ -101,9 +104,10 @@ public class SafeBrowsingTransforms {
|
||||
* HttpClients#createDefault()}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
EvaluateSafeBrowsingFn(String apiKey, Retrier retrier) {
|
||||
EvaluateSafeBrowsingFn(String apiKey, Retrier retrier, Clock clock) {
|
||||
this.apiKey = apiKey;
|
||||
this.retrier = retrier;
|
||||
this.clock = clock;
|
||||
closeableHttpClientSupplier = (Supplier & Serializable) HttpClients::createDefault;
|
||||
}
|
||||
|
||||
@@ -115,9 +119,10 @@ public class SafeBrowsingTransforms {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
EvaluateSafeBrowsingFn(
|
||||
String apiKey, Retrier retrier, Supplier<CloseableHttpClient> clientSupplier) {
|
||||
String apiKey, Retrier retrier, Clock clock, Supplier<CloseableHttpClient> clientSupplier) {
|
||||
this.apiKey = apiKey;
|
||||
this.retrier = retrier;
|
||||
this.clock = clock;
|
||||
closeableHttpClientSupplier = clientSupplier;
|
||||
}
|
||||
|
||||
@@ -126,7 +131,10 @@ public class SafeBrowsingTransforms {
|
||||
public void finishBundle(FinishBundleContext context) {
|
||||
if (!domainNameInfoBuffer.isEmpty()) {
|
||||
ImmutableSet<KV<DomainNameInfo, ThreatMatch>> results = evaluateAndFlush();
|
||||
results.forEach((kv) -> context.output(kv, Instant.now(), GlobalWindow.INSTANCE));
|
||||
results.forEach(
|
||||
(kv) ->
|
||||
context.output(
|
||||
kv, DateTimeUtils.toJodaInstant(clock.now()), GlobalWindow.INSTANCE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.model.reporting.Spec11ThreatMatch;
|
||||
import google.registry.model.reporting.Spec11ThreatMatch.ThreatType;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.UtilsModule;
|
||||
import jakarta.inject.Singleton;
|
||||
@@ -263,8 +264,9 @@ public class Spec11Pipeline implements Serializable {
|
||||
}
|
||||
|
||||
@Provides
|
||||
EvaluateSafeBrowsingFn provideSafeBrowsingFn(Spec11PipelineOptions options, Retrier retrier) {
|
||||
return new EvaluateSafeBrowsingFn(options.getSafeBrowsingApiKey(), retrier);
|
||||
EvaluateSafeBrowsingFn provideSafeBrowsingFn(
|
||||
Spec11PipelineOptions options, Retrier retrier, Clock clock) {
|
||||
return new EvaluateSafeBrowsingFn(options.getSafeBrowsingApiKey(), retrier, clock);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -24,7 +24,6 @@ import static com.google.common.util.concurrent.Futures.transformAsync;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
import static google.registry.bigquery.BigqueryUtils.toJobReferenceString;
|
||||
import static google.registry.config.RegistryConfig.getProjectId;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
||||
import com.google.api.client.http.AbstractInputStreamContent;
|
||||
@@ -58,6 +57,7 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||
import google.registry.bigquery.BigqueryUtils.DestinationFormat;
|
||||
import google.registry.bigquery.BigqueryUtils.TableType;
|
||||
import google.registry.bigquery.BigqueryUtils.WriteDisposition;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import google.registry.util.Sleeper;
|
||||
import google.registry.util.SqlTemplate;
|
||||
@@ -69,7 +69,6 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Class encapsulating parameters and state for accessing the Bigquery API. */
|
||||
@@ -94,6 +93,9 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
/** Bigquery client instance wrapped by this class. */
|
||||
private final Bigquery bigquery;
|
||||
|
||||
/** Clock instance for this connection. */
|
||||
private final Clock clock;
|
||||
|
||||
/** Executor service for bigquery jobs. */
|
||||
private ListeningExecutorService service;
|
||||
|
||||
@@ -109,8 +111,9 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
/** Duration to wait between polls for job status. */
|
||||
private Duration pollInterval = Duration.millis(1000);
|
||||
|
||||
BigqueryConnection(Bigquery bigquery) {
|
||||
BigqueryConnection(Bigquery bigquery, Clock clock) {
|
||||
this.bigquery = bigquery;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
/** Builder for a {@link BigqueryConnection}, since the latter is immutable once created. */
|
||||
@@ -118,8 +121,8 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
private BigqueryConnection instance;
|
||||
|
||||
@Inject
|
||||
Builder(Bigquery bigquery) {
|
||||
instance = new BigqueryConnection(bigquery);
|
||||
Builder(Bigquery bigquery, Clock clock) {
|
||||
instance = new BigqueryConnection(bigquery, clock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,6 +198,11 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
private final TableReference tableRef = new TableReference();
|
||||
private TableType type = TableType.TABLE;
|
||||
private WriteDisposition writeDisposition = WriteDisposition.WRITE_EMPTY;
|
||||
private final Clock clock;
|
||||
|
||||
public Builder(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
public Builder datasetId(String datasetId) {
|
||||
tableRef.setDatasetId(datasetId);
|
||||
@@ -217,7 +225,7 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
}
|
||||
|
||||
public Builder timeToLive(Duration duration) {
|
||||
this.table.setExpirationTime(DateTime.now(UTC).plus(duration).getMillis());
|
||||
this.table.setExpirationTime(clock.nowUtc().plus(duration).getMillis());
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -302,7 +310,7 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
|
||||
/** Returns a partially built DestinationTable with the default dataset and overwrite behavior. */
|
||||
public DestinationTable.Builder buildDestinationTable(String tableName) {
|
||||
return new DestinationTable.Builder()
|
||||
return new DestinationTable.Builder(clock)
|
||||
.datasetId(datasetId)
|
||||
.type(TableType.TABLE)
|
||||
.name(tableName)
|
||||
@@ -314,7 +322,7 @@ public class BigqueryConnection implements AutoCloseable {
|
||||
* temporary table dataset, with the default TTL and overwrite behavior.
|
||||
*/
|
||||
public DestinationTable.Builder buildTemporaryTable() {
|
||||
return new DestinationTable.Builder()
|
||||
return new DestinationTable.Builder(clock)
|
||||
.datasetId(TEMP_DATASET_NAME)
|
||||
.type(TableType.TABLE)
|
||||
.name(getRandomTableName())
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.keyring;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -21,7 +22,6 @@ import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.keyring.api.Keyring;
|
||||
import google.registry.keyring.secretmanager.SecretManagerKeyring;
|
||||
import jakarta.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Dagger module for {@link Keyring} */
|
||||
@Module
|
||||
@@ -38,9 +38,10 @@ public abstract class KeyringModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("cloudSqlReplicaInstanceConnectionName")
|
||||
public static Optional<String> provideCloudSqlReplicaInstanceConnectionName(Keyring keyring) {
|
||||
return Optional.ofNullable(keyring.getSqlReplicaConnectionName());
|
||||
@Config("cloudSqlReplicaInstanceConnectionNames")
|
||||
public static ImmutableList<String> provideCloudSqlReplicaInstanceConnectionNames(
|
||||
Keyring keyring) {
|
||||
return ImmutableList.copyOf(keyring.getSqlReplicaConnectionNames());
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.keyring.api;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||
@@ -151,9 +152,17 @@ public interface Keyring extends AutoCloseable {
|
||||
/** Returns the Cloud SQL connection name of the primary database instance. */
|
||||
String getSqlPrimaryConnectionName();
|
||||
|
||||
/** Returns the Cloud SQL connection name of the replica database instance. */
|
||||
/**
|
||||
* Returns the Cloud SQL connection name of the replica database instance.
|
||||
*
|
||||
* <p>Note: It is likely a better idea to use multiple replicas and {@link
|
||||
* #getSqlReplicaConnectionNames()} instead.
|
||||
*/
|
||||
String getSqlReplicaConnectionName();
|
||||
|
||||
/** Returns the Cloud SQL connection names of the replica database instances. */
|
||||
ImmutableList<String> getSqlReplicaConnectionNames();
|
||||
|
||||
// Don't throw so try-with-resources works better.
|
||||
@Override
|
||||
void close();
|
||||
|
||||
@@ -17,6 +17,8 @@ package google.registry.keyring.secretmanager;
|
||||
import static com.google.common.base.CaseFormat.LOWER_HYPHEN;
|
||||
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.keyring.api.KeySerializer;
|
||||
import google.registry.keyring.api.Keyring;
|
||||
import google.registry.keyring.api.KeyringException;
|
||||
@@ -66,7 +68,8 @@ public class SecretManagerKeyring implements Keyring {
|
||||
RDE_SSH_CLIENT_PUBLIC_STRING,
|
||||
SAFE_BROWSING_API_KEY,
|
||||
SQL_PRIMARY_CONN_NAME,
|
||||
SQL_REPLICA_CONN_NAME;
|
||||
SQL_REPLICA_CONN_NAME,
|
||||
SQL_REPLICA_CONN_NAMES;
|
||||
|
||||
String getLabel() {
|
||||
return UPPER_UNDERSCORE.to(LOWER_HYPHEN, name());
|
||||
@@ -157,7 +160,25 @@ public class SecretManagerKeyring implements Keyring {
|
||||
|
||||
@Override
|
||||
public String getSqlReplicaConnectionName() {
|
||||
return getString(StringKeyLabel.SQL_REPLICA_CONN_NAME);
|
||||
try {
|
||||
return getString(StringKeyLabel.SQL_REPLICA_CONN_NAME);
|
||||
} catch (KeyringException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<String> getSqlReplicaConnectionNames() {
|
||||
try {
|
||||
String names = getString(StringKeyLabel.SQL_REPLICA_CONN_NAMES);
|
||||
return ImmutableList.copyOf(
|
||||
Splitter.on('\n').trimResults().omitEmptyStrings().splitToList(names));
|
||||
} catch (KeyringException e) {
|
||||
String replicaConnectionName = getSqlReplicaConnectionName();
|
||||
return replicaConnectionName == null
|
||||
? ImmutableList.of()
|
||||
: ImmutableList.of(replicaConnectionName);
|
||||
}
|
||||
}
|
||||
|
||||
/** No persistent resources are maintained for this Keyring implementation. */
|
||||
|
||||
+5
@@ -34,6 +34,7 @@ import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringK
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SAFE_BROWSING_API_KEY;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SQL_PRIMARY_CONN_NAME;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SQL_REPLICA_CONN_NAME;
|
||||
import static google.registry.keyring.secretmanager.SecretManagerKeyring.StringKeyLabel.SQL_REPLICA_CONN_NAMES;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
@@ -134,6 +135,10 @@ public final class SecretManagerKeyringUpdater {
|
||||
return setString(name, SQL_REPLICA_CONN_NAME);
|
||||
}
|
||||
|
||||
public SecretManagerKeyringUpdater setSqlReplicaConnectionNames(String names) {
|
||||
return setString(names, SQL_REPLICA_CONN_NAMES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the secrets in the Secret Manager.
|
||||
*
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.model.eppcommon;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -28,7 +27,6 @@ import google.registry.model.domain.fee06.FeeInfoResponseExtensionV06;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import google.registry.xml.ValidationMode;
|
||||
import google.registry.xml.XmlException;
|
||||
import google.registry.xml.XmlTransformer;
|
||||
@@ -71,13 +69,9 @@ public class EppXmlTransformer {
|
||||
private static final XmlTransformer OUTPUT_TRANSFORMER =
|
||||
new XmlTransformer(getSchemas(), EppOutput.class);
|
||||
|
||||
// TODO(b/159033801): remove method and inline ALL_SCHEMA.
|
||||
@VisibleForTesting
|
||||
public static ImmutableList<String> getSchemas() {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)) {
|
||||
return ALL_SCHEMAS.stream()
|
||||
.filter(s -> !NON_PROD_SCHEMAS.contains(s))
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
return ALL_SCHEMAS;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,34 +129,31 @@ public final class PremiumListDao {
|
||||
|
||||
public static PremiumList save(String name, CurrencyUnit currencyUnit, List<String> inputData) {
|
||||
checkArgument(!inputData.isEmpty(), "New premium list data cannot be empty");
|
||||
return save(PremiumListUtils.parseToPremiumList(name, currencyUnit, inputData));
|
||||
tm().assertInTransaction();
|
||||
return save(
|
||||
PremiumListUtils.parseToPremiumList(
|
||||
name, currencyUnit, inputData, tm().getTransactionTime()));
|
||||
}
|
||||
|
||||
/** Saves the given premium list (and its premium list entries) to Cloud SQL. */
|
||||
public static PremiumList save(PremiumList premiumListToPersist) {
|
||||
PremiumList persisted =
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Make a new copy in each attempt to insert. See javadoc of the insert method for
|
||||
// more information.
|
||||
PremiumList premiumList = premiumListToPersist.asBuilder().build();
|
||||
tm().insert(premiumList);
|
||||
tm().getEntityManager().flush(); // This populates the revisionId.
|
||||
long revisionId = premiumList.getRevisionId();
|
||||
tm().assertInTransaction();
|
||||
// Make a new copy in each attempt to insert. See javadoc of the insert method for
|
||||
// more information.
|
||||
PremiumList premiumList = premiumListToPersist.asBuilder().build();
|
||||
tm().insert(premiumList);
|
||||
tm().getEntityManager().flush(); // This populates the revisionId.
|
||||
long revisionId = premiumList.getRevisionId();
|
||||
|
||||
if (!isNullOrEmpty(premiumList.getLabelsToPrices())) {
|
||||
ImmutableSet.Builder<PremiumEntry> entries = new ImmutableSet.Builder<>();
|
||||
premiumList
|
||||
.getLabelsToPrices()
|
||||
.forEach(
|
||||
(key, value) ->
|
||||
entries.add(PremiumEntry.create(revisionId, value, key)));
|
||||
tm().insertAll(entries.build());
|
||||
}
|
||||
return premiumList;
|
||||
});
|
||||
premiumListCache.invalidate(persisted.getName());
|
||||
return persisted;
|
||||
if (!isNullOrEmpty(premiumList.getLabelsToPrices())) {
|
||||
ImmutableSet.Builder<PremiumEntry> entries = new ImmutableSet.Builder<>();
|
||||
premiumList
|
||||
.getLabelsToPrices()
|
||||
.forEach((key, value) -> entries.add(PremiumEntry.create(revisionId, value, key)));
|
||||
tm().insertAll(entries.build());
|
||||
}
|
||||
premiumListCache.invalidate(premiumList.getName());
|
||||
return premiumList;
|
||||
}
|
||||
|
||||
public static void delete(PremiumList premiumList) {
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
package google.registry.model.tld.label;
|
||||
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import google.registry.model.tld.label.PremiumList.PremiumEntry;
|
||||
@@ -29,12 +27,12 @@ import org.joda.time.DateTime;
|
||||
public class PremiumListUtils {
|
||||
|
||||
public static PremiumList parseToPremiumList(
|
||||
String name, CurrencyUnit currencyUnit, List<String> inputData) {
|
||||
String name, CurrencyUnit currencyUnit, List<String> inputData, DateTime creationTime) {
|
||||
PremiumList partialPremiumList =
|
||||
new PremiumList.Builder()
|
||||
.setName(name)
|
||||
.setCurrency(currencyUnit)
|
||||
.setCreationTimestamp(DateTime.now(UTC))
|
||||
.setCreationTimestamp(creationTime)
|
||||
.build();
|
||||
ImmutableMap<String, PremiumEntry> prices = partialPremiumList.parse(inputData);
|
||||
Map<String, BigDecimal> priceAmounts = Maps.transformValues(prices, PremiumEntry::getValue);
|
||||
|
||||
@@ -24,10 +24,10 @@ import static google.registry.model.tld.label.ReservationType.FULLY_BLOCKED;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
@@ -47,7 +47,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A list of reserved domain labels that are blocked from being registered for various reasons.
|
||||
@@ -234,7 +233,7 @@ public final class ReservedList
|
||||
*/
|
||||
private static ImmutableSet<ReservedListEntry> getReservedListEntries(
|
||||
String label, String tldStr) {
|
||||
DateTime startTime = DateTime.now(UTC);
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
Tld tld = Tld.get(checkNotNull(tldStr, "tld must not be null"));
|
||||
ImmutableSet.Builder<ReservedListEntry> entriesBuilder = new ImmutableSet.Builder<>();
|
||||
ImmutableSet.Builder<MetricsReservedListMatch> metricMatchesBuilder =
|
||||
@@ -251,9 +250,7 @@ public final class ReservedList
|
||||
}
|
||||
ImmutableSet<ReservedListEntry> entries = entriesBuilder.build();
|
||||
DomainLabelMetrics.recordReservedListCheckOutcome(
|
||||
tldStr,
|
||||
metricMatchesBuilder.build(),
|
||||
DateTime.now(UTC).getMillis() - startTime.getMillis());
|
||||
tldStr, metricMatchesBuilder.build(), stopwatch.elapsed().toMillis());
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.persistence;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.config.RegistryConfig.getHibernateConnectionIsolation;
|
||||
import static google.registry.config.RegistryConfig.getHibernateHikariConnectionTimeout;
|
||||
import static google.registry.config.RegistryConfig.getHibernateHikariIdleTimeout;
|
||||
@@ -28,6 +29,7 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import dagger.BindsOptionalOf;
|
||||
@@ -36,6 +38,7 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.persistence.transaction.CloudSqlCredentialSupplier;
|
||||
import google.registry.persistence.transaction.DelegatingReplicaJpaTransactionManager;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerImpl;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
@@ -59,6 +62,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
import org.hibernate.cfg.Environment;
|
||||
@@ -264,16 +268,13 @@ public abstract class PersistenceModule {
|
||||
static JpaTransactionManager provideReadOnlyReplicaJpaTm(
|
||||
SqlCredentialStore credentialStore,
|
||||
@PartialCloudSqlConfigs ImmutableMap<String, String> cloudSqlConfigs,
|
||||
@Config("cloudSqlReplicaInstanceConnectionName")
|
||||
Optional<String> replicaInstanceConnectionName,
|
||||
Clock clock) {
|
||||
@Config("cloudSqlReplicaInstanceConnectionNames")
|
||||
ImmutableList<String> replicaInstanceConnectionNames,
|
||||
Clock clock,
|
||||
Random random) {
|
||||
HashMap<String, String> overrides = Maps.newHashMap(cloudSqlConfigs);
|
||||
setSqlCredential(credentialStore, new RobotUser(RobotId.NOMULUS), overrides);
|
||||
replicaInstanceConnectionName.ifPresent(
|
||||
name -> overrides.put(HIKARI_DS_CLOUD_SQL_INSTANCE, name));
|
||||
overrides.put(
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ.name());
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock, true);
|
||||
return createReplicaJpaTm(overrides, replicaInstanceConnectionNames, clock, random);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -281,15 +282,34 @@ public abstract class PersistenceModule {
|
||||
@BeamReadOnlyReplicaJpaTm
|
||||
static JpaTransactionManager provideBeamReadOnlyReplicaJpaTm(
|
||||
@BeamPipelineCloudSqlConfigs ImmutableMap<String, String> beamCloudSqlConfigs,
|
||||
@Config("cloudSqlReplicaInstanceConnectionName")
|
||||
Optional<String> replicaInstanceConnectionName,
|
||||
Clock clock) {
|
||||
@Config("cloudSqlReplicaInstanceConnectionNames")
|
||||
ImmutableList<String> replicaInstanceConnectionNames,
|
||||
Clock clock,
|
||||
Random random) {
|
||||
HashMap<String, String> overrides = Maps.newHashMap(beamCloudSqlConfigs);
|
||||
replicaInstanceConnectionName.ifPresent(
|
||||
name -> overrides.put(HIKARI_DS_CLOUD_SQL_INSTANCE, name));
|
||||
overrides.put(
|
||||
return createReplicaJpaTm(overrides, replicaInstanceConnectionNames, clock, random);
|
||||
}
|
||||
|
||||
private static JpaTransactionManager createReplicaJpaTm(
|
||||
Map<String, String> baseOverrides,
|
||||
ImmutableList<String> replicaInstanceConnectionNames,
|
||||
Clock clock,
|
||||
Random random) {
|
||||
baseOverrides.put(
|
||||
Environment.ISOLATION, TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ.name());
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock, true);
|
||||
if (replicaInstanceConnectionNames.isEmpty()) {
|
||||
return new JpaTransactionManagerImpl(create(baseOverrides), clock, true);
|
||||
}
|
||||
ImmutableList<JpaTransactionManager> replicas =
|
||||
replicaInstanceConnectionNames.stream()
|
||||
.map(
|
||||
name -> {
|
||||
HashMap<String, String> overrides = Maps.newHashMap(baseOverrides);
|
||||
overrides.put(HIKARI_DS_CLOUD_SQL_INSTANCE, name);
|
||||
return new JpaTransactionManagerImpl(create(overrides), clock, true);
|
||||
})
|
||||
.collect(toImmutableList());
|
||||
return new DelegatingReplicaJpaTransactionManager(replicas, random);
|
||||
}
|
||||
|
||||
/** Constructs the {@link EntityManagerFactory} instance. */
|
||||
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
// Copyright 2026 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.transaction;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.VKey;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Query;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.metamodel.Metamodel;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A {@link JpaTransactionManager} that load-balances across multiple read-only replicas.
|
||||
*
|
||||
* <p>For each top-level transaction, one replica is chosen and used for the duration of the
|
||||
* transaction. For non-transactional methods, a replica is chosen for each call.
|
||||
*/
|
||||
public class DelegatingReplicaJpaTransactionManager implements JpaTransactionManager {
|
||||
|
||||
private final ImmutableList<JpaTransactionManager> replicas;
|
||||
private final Random random;
|
||||
private static final AtomicLong nextId = new AtomicLong(1);
|
||||
|
||||
private static final ThreadLocal<JpaTransactionManager> activeReplica = new ThreadLocal<>();
|
||||
|
||||
public DelegatingReplicaJpaTransactionManager(
|
||||
ImmutableList<JpaTransactionManager> replicas, Random random) {
|
||||
checkArgument(!replicas.isEmpty(), "At least one replica must be provided");
|
||||
this.replicas = replicas;
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
private JpaTransactionManager getReplica() {
|
||||
JpaTransactionManager replica = activeReplica.get();
|
||||
if (replica != null) {
|
||||
return replica;
|
||||
}
|
||||
return getRandomReplica();
|
||||
}
|
||||
|
||||
private <T> T runMaybeAssigningReplica(Function<JpaTransactionManager, T> work) {
|
||||
JpaTransactionManager existing = activeReplica.get();
|
||||
if (existing != null) {
|
||||
return work.apply(existing);
|
||||
}
|
||||
JpaTransactionManager replica = getRandomReplica();
|
||||
activeReplica.set(replica);
|
||||
try {
|
||||
return work.apply(replica);
|
||||
} finally {
|
||||
activeReplica.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private JpaTransactionManager getRandomReplica() {
|
||||
return replicas.get(random.nextInt(replicas.size()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inTransaction() {
|
||||
var replica = activeReplica.get();
|
||||
return replica != null && replica.inTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertInTransaction() {
|
||||
JpaTransactionManager replica = activeReplica.get();
|
||||
if (replica == null) {
|
||||
throw new IllegalStateException("Not in a transaction");
|
||||
}
|
||||
replica.assertInTransaction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long allocateId() {
|
||||
return nextId.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(Callable<T> work) {
|
||||
return transact(null, work, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(TransactionIsolationLevel isolationLevel, Callable<T> work) {
|
||||
return transact(isolationLevel, work, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(Callable<T> work) {
|
||||
return transactNoRetry(null, work, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(TransactionIsolationLevel isolationLevel, Callable<T> work) {
|
||||
return transactNoRetry(isolationLevel, work, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T reTransact(Callable<T> work) {
|
||||
return runMaybeAssigningReplica(replica -> replica.reTransact(work));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(ThrowingRunnable work) {
|
||||
transact(
|
||||
() -> {
|
||||
work.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transact(TransactionIsolationLevel isolationLevel, ThrowingRunnable work) {
|
||||
transact(
|
||||
isolationLevel,
|
||||
() -> {
|
||||
work.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reTransact(ThrowingRunnable work) {
|
||||
reTransact(
|
||||
() -> {
|
||||
work.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public DateTime getTransactionTime() {
|
||||
return getReplica().getTransactionTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getTxTime() {
|
||||
return getReplica().getTxTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(Object entity) {
|
||||
getReplica().insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAll(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertAll(ImmutableObject... entities) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object entity) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(ImmutableObject... entities) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Object entity) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(ImmutableCollection<?> entities) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(ImmutableObject... entities) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(Object entity) {
|
||||
return getReplica().exists(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean exists(VKey<T> key) {
|
||||
return getReplica().exists(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> loadByKeyIfPresent(VKey<T> key) {
|
||||
return getReplica().loadByKeyIfPresent(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableMap<VKey<? extends T>, T> loadByKeysIfPresent(
|
||||
Iterable<? extends VKey<? extends T>> keys) {
|
||||
return getReplica().loadByKeysIfPresent(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadByEntitiesIfPresent(Iterable<T> entities) {
|
||||
return getReplica().loadByEntitiesIfPresent(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T loadByKey(VKey<T> key) {
|
||||
return getReplica().loadByKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableMap<VKey<? extends T>, T> loadByKeys(
|
||||
Iterable<? extends VKey<? extends T>> keys) {
|
||||
return getReplica().loadByKeys(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T loadByEntity(T entity) {
|
||||
return getReplica().loadByEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadByEntities(Iterable<T> entities) {
|
||||
return getReplica().loadByEntities(entities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ImmutableList<T> loadAllOf(Class<T> clazz) {
|
||||
return getReplica().loadAllOf(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Stream<T> loadAllOfStream(Class<T> clazz) {
|
||||
return getReplica().loadAllOfStream(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> loadSingleton(Class<T> clazz) {
|
||||
return getReplica().loadSingleton(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(VKey<?> key) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Iterable<? extends VKey<?>> keys) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T delete(T entity) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> QueryComposer<T> createQueryComposer(Class<T> entity) {
|
||||
return getReplica().createQueryComposer(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityManager getStandaloneEntityManager() {
|
||||
return getReplica().getStandaloneEntityManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metamodel getMetaModel() {
|
||||
return getReplica().getMetaModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityManager getEntityManager() {
|
||||
return getReplica().getEntityManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TypedQuery<T> query(String sqlString, Class<T> resultClass) {
|
||||
return getReplica().query(sqlString, resultClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TypedQuery<T> criteriaQuery(CriteriaQuery<T> criteriaQuery) {
|
||||
return getReplica().criteriaQuery(criteriaQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query query(String sqlString) {
|
||||
return getReplica().query(sqlString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void assertDelete(VKey<T> key) {
|
||||
throw new UnsupportedOperationException("This is a replica database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardown() {
|
||||
for (JpaTransactionManager replica : replicas) {
|
||||
replica.teardown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionIsolationLevel getDefaultTransactionIsolationLevel() {
|
||||
return replicas.get(0).getDefaultTransactionIsolationLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransactionIsolationLevel getCurrentTransactionIsolationLevel() {
|
||||
return getReplica().getCurrentTransactionIsolationLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transact(
|
||||
TransactionIsolationLevel isolationLevel, Callable<T> work, boolean logSqlStatements) {
|
||||
return runMaybeAssigningReplica(
|
||||
replica -> replica.transact(isolationLevel, work, logSqlStatements));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T transactNoRetry(
|
||||
TransactionIsolationLevel isolationLevel, Callable<T> work, boolean logSqlStatements) {
|
||||
return runMaybeAssigningReplica(
|
||||
replica -> replica.transactNoRetry(isolationLevel, work, logSqlStatements));
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.tld.Tlds.findTldForNameOrThrow;
|
||||
import static google.registry.pricing.PricingEngineProxy.getDomainCreateCost;
|
||||
import static google.registry.util.StringGenerator.DEFAULT_PASSWORD_LENGTH;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -30,7 +29,6 @@ import google.registry.util.StringGenerator;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** A command to create a new anchor tenant domain. */
|
||||
@Parameters(separators = " =", commandDescription = "Provision a domain for an anchor tenant.")
|
||||
@@ -86,7 +84,7 @@ final class CreateAnchorTenantCommand extends MutatingEppToolCommand {
|
||||
|
||||
Money cost = null;
|
||||
if (fee) {
|
||||
cost = getDomainCreateCost(domainName, DateTime.now(UTC), DEFAULT_ANCHOR_TENANT_PERIOD_YEARS);
|
||||
cost = getDomainCreateCost(domainName, clock.nowUtc(), DEFAULT_ANCHOR_TENANT_PERIOD_YEARS);
|
||||
}
|
||||
|
||||
setSoyTemplate(CreateAnchorTenantSoyInfo.getInstance(),
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.tools;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -28,7 +27,6 @@ import google.registry.util.StringGenerator;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** A command to create a new domain via EPP. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a new domain via EPP.")
|
||||
@@ -64,7 +62,7 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
for (String domain : domains) {
|
||||
String currency = null;
|
||||
String cost = null;
|
||||
DomainPrices prices = getPricesForDomainName(domain, DateTime.now(UTC));
|
||||
DomainPrices prices = getPricesForDomainName(domain, clock.nowUtc());
|
||||
|
||||
// Check if the domain is premium and set the fee on the create command if so.
|
||||
if (prices.isPremium()) {
|
||||
|
||||
@@ -14,10 +14,14 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.tools.params.PathParameter;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -29,6 +33,8 @@ import org.joda.money.CurrencyUnit;
|
||||
*/
|
||||
abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand {
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
protected List<String> inputData;
|
||||
protected CurrencyUnit currency;
|
||||
@@ -54,7 +60,7 @@ abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand {
|
||||
String.format("Saved premium list %s with %d entries.", name, inputData.size());
|
||||
try {
|
||||
logger.atInfo().log("Saving premium list for TLD %s.", name);
|
||||
PremiumListDao.save(name, currency, inputData);
|
||||
tm().transact(() -> PremiumListDao.save(name, currency, inputData));
|
||||
logger.atInfo().log(message);
|
||||
} catch (Throwable e) {
|
||||
message = "Unexpected error saving premium list from nomulus tool command.";
|
||||
|
||||
@@ -21,7 +21,6 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.util.RegistrarUtils.normalizeRegistrarName;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Ascii;
|
||||
@@ -37,6 +36,7 @@ import google.registry.tools.params.OptionalStringParameter;
|
||||
import google.registry.tools.params.PathParameter.InputFile;
|
||||
import google.registry.tools.params.StringListParameter;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -56,6 +56,8 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
|
||||
@Inject CertificateChecker certificateChecker;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Parameter(description = "Client identifier of the registrar account", required = true)
|
||||
List<String> mainParameters;
|
||||
|
||||
@@ -272,7 +274,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
@Override
|
||||
protected final void init() throws Exception {
|
||||
initRegistrarCommand();
|
||||
DateTime now = DateTime.now(UTC);
|
||||
DateTime now = clock.nowUtc();
|
||||
for (String clientId : mainParameters) {
|
||||
Registrar oldRegistrar = getOldRegistrar(clientId);
|
||||
Registrar.Builder builder =
|
||||
|
||||
@@ -19,6 +19,8 @@ import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.tld.label.ReservedList;
|
||||
import google.registry.model.tld.label.ReservedListDao;
|
||||
import google.registry.tools.params.PathParameter;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -29,6 +31,8 @@ import javax.annotation.Nullable;
|
||||
*/
|
||||
public abstract class CreateOrUpdateReservedListCommand extends ConfirmingCommand {
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.tld.Tlds.assertTldExists;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -51,7 +50,7 @@ final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand
|
||||
if (!override) {
|
||||
validateListName(name);
|
||||
}
|
||||
DateTime now = DateTime.now(UTC);
|
||||
DateTime now = clock.nowUtc();
|
||||
List<String> allLines = Files.readAllLines(input, UTF_8);
|
||||
reservedList =
|
||||
new ReservedList.Builder()
|
||||
|
||||
@@ -38,6 +38,8 @@ import com.google.template.soy.data.SoyRecord;
|
||||
import com.google.template.soy.parseinfo.SoyFileInfo;
|
||||
import com.google.template.soy.parseinfo.SoyTemplateInfo;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
@@ -49,6 +51,8 @@ import java.util.Objects;
|
||||
/** A command to execute an epp command. */
|
||||
abstract class EppToolCommand extends ConfirmingCommand implements CommandWithConnection {
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Parameter(
|
||||
names = {"-u", "--superuser"},
|
||||
description = "Run in superuser mode")
|
||||
|
||||
@@ -56,8 +56,7 @@ final class GenerateDnsReportCommand implements Command {
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
@Inject Clock clock;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.joda.time.Duration.standardMinutes;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
@@ -23,6 +22,8 @@ import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.tools.params.DateParameter;
|
||||
import google.registry.tools.server.GenerateZoneFilesAction;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -40,10 +41,13 @@ final class GenerateZoneFilesCommand implements CommandWithConnection {
|
||||
// Default to latest midnight that's at least 2 minutes ago.
|
||||
@Parameter(
|
||||
names = "--export_date",
|
||||
description = "The date to generate the file for (defaults to today, or yesterday if run "
|
||||
+ "before 00:02).",
|
||||
description =
|
||||
"The date to generate the file for (defaults to today, or yesterday if run "
|
||||
+ "before 00:02).",
|
||||
validateWith = DateParameter.class)
|
||||
private DateTime exportDate = DateTime.now(UTC).minus(standardMinutes(2)).withTimeAtStartOfDay();
|
||||
private DateTime exportDate;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
private ServiceConnection connection;
|
||||
|
||||
@@ -54,6 +58,9 @@ final class GenerateZoneFilesCommand implements CommandWithConnection {
|
||||
|
||||
@Override
|
||||
public void run() throws IOException {
|
||||
if (exportDate == null) {
|
||||
exportDate = clock.nowUtc().minus(standardMinutes(2)).withTimeAtStartOfDay();
|
||||
}
|
||||
assertTldsExist(mainParameters);
|
||||
ImmutableMap<String, Object> params = ImmutableMap.of(
|
||||
"tlds", mainParameters,
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.tools;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -26,7 +25,9 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.CommandUtilities.ResourceType;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.xml.XmlTransformer;
|
||||
import jakarta.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command to show history entries. */
|
||||
@@ -35,6 +36,8 @@ import org.joda.time.DateTime;
|
||||
commandDescription = "Show history entries that occurred in a given time range")
|
||||
final class GetHistoryEntriesCommand implements Command {
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Parameter(
|
||||
names = {"-a", "--after"},
|
||||
description = "Only show history entries that occurred at or after this time")
|
||||
@@ -58,7 +61,7 @@ final class GetHistoryEntriesCommand implements Command {
|
||||
checkArgument(
|
||||
type != null && uniqueId != null,
|
||||
"If either of 'type' or 'id' are set then both must be");
|
||||
VKey<? extends EppResource> parentKey = type.getKey(uniqueId, DateTime.now(UTC));
|
||||
VKey<? extends EppResource> parentKey = type.getKey(uniqueId, clock.nowUtc());
|
||||
historyEntries = HistoryEntryDao.loadHistoryObjectsForResource(parentKey, after, before);
|
||||
} else {
|
||||
historyEntries = HistoryEntryDao.loadAllHistoryObjects(after, before);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.keyring.api.KeySerializer;
|
||||
@@ -95,6 +96,10 @@ final class GetKeyringSecretCommand implements Command {
|
||||
out.write(KeySerializer.serializeString(keyring.getSqlPrimaryConnectionName()));
|
||||
case SQL_REPLICA_CONN_NAME ->
|
||||
out.write(KeySerializer.serializeString(keyring.getSqlReplicaConnectionName()));
|
||||
case SQL_REPLICA_CONN_NAMES ->
|
||||
out.write(
|
||||
KeySerializer.serializeString(
|
||||
String.join("\n", keyring.getSqlReplicaConnectionNames())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.flows.ResourceFlowUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.tools.soy.DomainRenewSoyInfo;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.List;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
@@ -63,9 +61,6 @@ final class RenewDomainCommand extends MutatingEppToolCommand {
|
||||
arity = 1)
|
||||
Boolean requestedByRegistrar;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("YYYY-MM-dd");
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,8 +37,6 @@ import google.registry.model.host.Host;
|
||||
import google.registry.tools.params.NameserversParameter;
|
||||
import google.registry.tools.soy.DomainRenewSoyInfo;
|
||||
import google.registry.tools.soy.UniformRapidSuspensionSoyInfo;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.xml.bind.annotation.adapters.HexBinaryAdapter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -122,8 +120,6 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
/** Set of status values to remove. */
|
||||
ImmutableSet<String> removeStatuses;
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand()
|
||||
throws ResourceFlowUtils.ResourceDoesNotExistException {
|
||||
|
||||
@@ -33,8 +33,6 @@ import google.registry.model.domain.GracePeriodBase;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.tools.params.NameserversParameter;
|
||||
import google.registry.tools.soy.DomainUpdateSoyInfo;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -49,8 +47,6 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject Clock clock;
|
||||
|
||||
@Parameter(names = "--statuses", description = "Comma-separated list of statuses to set.")
|
||||
private List<String> statuses = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -100,6 +100,8 @@ final class UpdateKeyringSecretCommand implements Command {
|
||||
secretManagerKeyringUpdater.setSqlPrimaryConnectionName(deserializeString(input));
|
||||
case SQL_REPLICA_CONN_NAME ->
|
||||
secretManagerKeyringUpdater.setSqlReplicaConnectionName(deserializeString(input));
|
||||
case SQL_REPLICA_CONN_NAMES ->
|
||||
secretManagerKeyringUpdater.setSqlReplicaConnectionNames(deserializeString(input));
|
||||
}
|
||||
|
||||
secretManagerKeyringUpdater.update();
|
||||
|
||||
@@ -62,7 +62,8 @@ class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||
checkArgument(!inputData.isEmpty(), "New premium list data cannot be empty");
|
||||
currency = existingList.getCurrency();
|
||||
PremiumList updatedPremiumList = PremiumListUtils.parseToPremiumList(name, currency, inputData);
|
||||
PremiumList updatedPremiumList =
|
||||
PremiumListUtils.parseToPremiumList(name, currency, inputData, clock.nowUtc());
|
||||
if (!existingList
|
||||
.getLabelsToPrices()
|
||||
.entrySet()
|
||||
|
||||
@@ -38,5 +38,6 @@ public enum KeyringKeyName {
|
||||
RDE_STAGING_PUBLIC_KEY,
|
||||
SAFE_BROWSING_API_KEY,
|
||||
SQL_PRIMARY_CONN_NAME,
|
||||
SQL_REPLICA_CONN_NAME
|
||||
SQL_REPLICA_CONN_NAME,
|
||||
SQL_REPLICA_CONN_NAMES
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.ui.server.console;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
@@ -35,7 +34,6 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVPrinter;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Action(
|
||||
service = Service.CONSOLE,
|
||||
@@ -86,7 +84,7 @@ public class ConsoleDumDownloadAction extends ConsoleApiAction {
|
||||
.setHeader("Cache-Control", "max-age=86400"); // 86400 seconds = 1 day
|
||||
consoleApiParams
|
||||
.response()
|
||||
.setDateHeader("Expires", DateTime.now(UTC).withTimeAtStartOfDay().plusDays(1));
|
||||
.setDateHeader("Expires", clock.nowUtc().withTimeAtStartOfDay().plusDays(1));
|
||||
|
||||
try (var writer = consoleApiParams.response().getWriter()) {
|
||||
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT);
|
||||
|
||||
@@ -45,6 +45,7 @@ import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.time.Instant;
|
||||
@@ -62,9 +63,11 @@ class DeleteProberDataActionTest {
|
||||
|
||||
private static final DateTime DELETION_TIME = DateTime.parse("2010-01-01T00:00:00.000Z");
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.now(UTC));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension
|
||||
final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension();
|
||||
@@ -94,7 +97,9 @@ class DeleteProberDataActionTest {
|
||||
}
|
||||
|
||||
private void resetAction() {
|
||||
action = new DeleteProberDataAction(false, ImmutableSet.of(), Optional.empty(), "TheRegistrar");
|
||||
action =
|
||||
new DeleteProberDataAction(
|
||||
false, ImmutableSet.of(), Optional.empty(), "TheRegistrar", clock);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -124,7 +129,7 @@ class DeleteProberDataActionTest {
|
||||
Set<ImmutableObject> oaEntities = persistLotsOfDomains("oa-canary.test");
|
||||
// Create action with batch size of 3
|
||||
DeleteProberDataAction batchedAction =
|
||||
new DeleteProberDataAction(false, ImmutableSet.of(), Optional.of(3), "TheRegistrar");
|
||||
new DeleteProberDataAction(false, ImmutableSet.of(), Optional.of(3), "TheRegistrar", clock);
|
||||
batchedAction.run();
|
||||
assertAllAbsent(ibEntities);
|
||||
assertAllAbsent(oaEntities);
|
||||
|
||||
+2
-1
@@ -97,7 +97,8 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
EXPIRATION_WARNING_EMAIL_SUBJECT_TEXT,
|
||||
sendEmailService,
|
||||
certificateChecker,
|
||||
response);
|
||||
response,
|
||||
clock);
|
||||
|
||||
sampleRegistrar =
|
||||
persistResource(createRegistrar("clientId", "sampleRegistrar", null, null).build());
|
||||
|
||||
@@ -85,10 +85,13 @@ class SafeBrowsingTransformsTest {
|
||||
private final CloseableHttpClient mockHttpClient =
|
||||
mock(CloseableHttpClient.class, withSettings().serializable());
|
||||
|
||||
private final FakeClock clock = new FakeClock();
|
||||
|
||||
private final EvaluateSafeBrowsingFn safeBrowsingFn =
|
||||
new EvaluateSafeBrowsingFn(
|
||||
"API_KEY",
|
||||
new Retrier(new FakeSleeper(new FakeClock()), 1),
|
||||
new Retrier(new FakeSleeper(clock), 1),
|
||||
clock,
|
||||
Suppliers.ofInstance(mockHttpClient));
|
||||
|
||||
@RegisterExtension
|
||||
|
||||
@@ -187,7 +187,8 @@ class Spec11PipelineTest {
|
||||
EvaluateSafeBrowsingFn safeBrowsingFn =
|
||||
new EvaluateSafeBrowsingFn(
|
||||
SAFE_BROWSING_API_KEY,
|
||||
new Retrier(new FakeSleeper(new FakeClock()), 1),
|
||||
new Retrier(new FakeSleeper(fakeClock), 1),
|
||||
fakeClock,
|
||||
Suppliers.ofInstance(mockHttpClient));
|
||||
when(mockHttpClient.execute(any(HttpPost.class))).thenAnswer(new HttpResponder());
|
||||
Spec11Pipeline spec11Pipeline = new Spec11Pipeline(options, safeBrowsingFn);
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.export;
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.export.ExportPremiumTermsAction.EXPORT_MIME_TYPE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.deleteTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -75,7 +76,7 @@ public class ExportPremiumTermsActionTest {
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
createTld("tld");
|
||||
PremiumList pl = PremiumListDao.save("pl-name", USD, PREMIUM_NAMES);
|
||||
PremiumList pl = tm().transact(() -> PremiumListDao.save("pl-name", USD, PREMIUM_NAMES));
|
||||
persistResource(
|
||||
Tld.get("tld").asBuilder().setPremiumList(pl).setDriveFolderId("folder_id").build());
|
||||
when(driveConnection.createOrUpdateFile(
|
||||
|
||||
+17
-12
@@ -24,6 +24,7 @@ import static google.registry.model.reporting.DomainTransactionRecord.Transactio
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_APPROVE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEventsForResource;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.deleteTestDomain;
|
||||
@@ -512,12 +513,14 @@ class DomainTransferApproveFlowTest
|
||||
@Test
|
||||
void testSuccess_nonpremiumPriceRenewalBehavior_carriesOver() throws Exception {
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build()));
|
||||
persistResource(Tld.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
@@ -558,12 +561,14 @@ class DomainTransferApproveFlowTest
|
||||
@Test
|
||||
void testSuccess_specifiedPriceRenewalBehavior_carriesOver() throws Exception {
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build()));
|
||||
persistResource(Tld.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
|
||||
+16
-12
@@ -1160,12 +1160,14 @@ class DomainTransferRequestFlowTest
|
||||
throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build()));
|
||||
persistResource(Tld.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
@@ -1214,12 +1216,14 @@ class DomainTransferRequestFlowTest
|
||||
throws Exception {
|
||||
setupDomain("example", "tld");
|
||||
PremiumList pl =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setCurrency(USD)
|
||||
.setName("tld")
|
||||
.setLabelsToPrices(ImmutableMap.of("example", new BigDecimal("67.89")))
|
||||
.build()));
|
||||
persistResource(Tld.get("tld").asBuilder().setPremiumList(pl).build());
|
||||
domain = loadByEntity(domain);
|
||||
persistResource(
|
||||
|
||||
+17
@@ -120,6 +120,23 @@ public class SecretManagerKeyringUpdaterTest {
|
||||
verifyPersistedSecret("sql-replica-conn-name", name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sqlReplicaConnectionNames() {
|
||||
String names = "name1\nname2";
|
||||
updater.setSqlReplicaConnectionNames(names).update();
|
||||
|
||||
assertThat(keyring.getSqlReplicaConnectionNames()).containsExactly("name1", "name2").inOrder();
|
||||
verifyPersistedSecret("sql-replica-conn-names", names);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sqlReplicaConnectionNames_fallback() {
|
||||
String name = "name";
|
||||
updater.setSqlReplicaConnectionName(name).update();
|
||||
|
||||
assertThat(keyring.getSqlReplicaConnectionNames()).containsExactly(name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void marksdbDnlLoginAndPassword() {
|
||||
String secret = "marksdbDnlLoginAndPassword";
|
||||
|
||||
@@ -82,11 +82,11 @@ class EppXmlTransformerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSchemas_inProduction_skipsFee1Point0() {
|
||||
void testSchemas_inProduction_includesFee1Point0() {
|
||||
var currentEnv = RegistryEnvironment.get();
|
||||
try {
|
||||
RegistryEnvironment.PRODUCTION.setup();
|
||||
assertThat(EppXmlTransformer.getSchemas()).doesNotContain("fee-std-v1.xsd");
|
||||
assertThat(EppXmlTransformer.getSchemas()).contains("fee-std-v1.xsd");
|
||||
} finally {
|
||||
currentEnv.setup();
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class PremiumListDaoTest {
|
||||
|
||||
@Test
|
||||
void saveNew_worksSuccessfully() {
|
||||
PremiumListDao.save(testList);
|
||||
tm().transact(() -> PremiumListDao.save(testList));
|
||||
tm().transact(
|
||||
() -> {
|
||||
Optional<PremiumList> persistedListOpt = PremiumListDao.getLatestRevision("testname");
|
||||
@@ -118,24 +118,26 @@ public class PremiumListDaoTest {
|
||||
|
||||
@Test
|
||||
void update_worksSuccessfully() {
|
||||
PremiumListDao.save(testList);
|
||||
tm().transact(() -> PremiumListDao.save(testList));
|
||||
Optional<PremiumList> persistedList = PremiumListDao.getLatestRevision("testname");
|
||||
assertThat(persistedList).isPresent();
|
||||
long firstRevisionId = persistedList.get().getRevisionId();
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("testname")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(
|
||||
ImmutableMap.of(
|
||||
"save",
|
||||
BigDecimal.valueOf(55343.12),
|
||||
"new",
|
||||
BigDecimal.valueOf(0.01),
|
||||
"silver",
|
||||
BigDecimal.valueOf(30.03)))
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("testname")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(
|
||||
ImmutableMap.of(
|
||||
"save",
|
||||
BigDecimal.valueOf(55343.12),
|
||||
"new",
|
||||
BigDecimal.valueOf(0.01),
|
||||
"silver",
|
||||
BigDecimal.valueOf(30.03)))
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build()));
|
||||
tm().transact(
|
||||
() -> {
|
||||
Optional<PremiumList> savedListOpt = PremiumListDao.getLatestRevision("testname");
|
||||
@@ -158,7 +160,7 @@ public class PremiumListDaoTest {
|
||||
@Test
|
||||
void checkExists_worksSuccessfully() {
|
||||
assertThat(PremiumListDao.getLatestRevision("testname")).isEmpty();
|
||||
PremiumListDao.save(testList);
|
||||
tm().transact(() -> PremiumListDao.save(testList));
|
||||
assertThat(PremiumListDao.getLatestRevision("testname")).isPresent();
|
||||
}
|
||||
|
||||
@@ -169,20 +171,24 @@ public class PremiumListDaoTest {
|
||||
|
||||
@Test
|
||||
void getLatestRevision_worksSuccessfully() {
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(ImmutableMap.of("wrong", BigDecimal.valueOf(1000.50)))
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build());
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(TEST_PRICES)
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(ImmutableMap.of("wrong", BigDecimal.valueOf(1000.50)))
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build()));
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(TEST_PRICES)
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build()));
|
||||
tm().transact(
|
||||
() -> {
|
||||
Optional<PremiumList> persistedList = PremiumListDao.getLatestRevision("list1");
|
||||
@@ -196,13 +202,15 @@ public class PremiumListDaoTest {
|
||||
|
||||
@Test
|
||||
void getLabelsToPrices_worksForJpy() {
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(JPY)
|
||||
.setLabelsToPrices(TEST_PRICES)
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("list1")
|
||||
.setCurrency(JPY)
|
||||
.setLabelsToPrices(TEST_PRICES)
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build()));
|
||||
tm().transact(
|
||||
() -> {
|
||||
PremiumList premiumList = PremiumListDao.getLatestRevision("list1").get();
|
||||
@@ -220,13 +228,15 @@ public class PremiumListDaoTest {
|
||||
@Test
|
||||
void getPremiumPrice_worksSuccessfully() {
|
||||
PremiumList premiumList =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("premlist")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(TEST_PRICES)
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("premlist")
|
||||
.setCurrency(USD)
|
||||
.setLabelsToPrices(TEST_PRICES)
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build()));
|
||||
persistResource(newTld("foobar", "FOOBAR").asBuilder().setPremiumList(premiumList).build());
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "silver")).hasValue(Money.of(USD, 10.23));
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "gold")).hasValue(Money.of(USD, 1305.47));
|
||||
@@ -236,20 +246,22 @@ public class PremiumListDaoTest {
|
||||
@Test
|
||||
void testGetPremiumPrice_worksForJPY() {
|
||||
PremiumList premiumList =
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("premlist")
|
||||
.setCurrency(JPY)
|
||||
.setLabelsToPrices(
|
||||
ImmutableMap.of(
|
||||
"silver",
|
||||
BigDecimal.valueOf(10.00),
|
||||
"gold",
|
||||
BigDecimal.valueOf(1000.0),
|
||||
"palladium",
|
||||
BigDecimal.valueOf(15000)))
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build());
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
new PremiumList.Builder()
|
||||
.setName("premlist")
|
||||
.setCurrency(JPY)
|
||||
.setLabelsToPrices(
|
||||
ImmutableMap.of(
|
||||
"silver",
|
||||
BigDecimal.valueOf(10.00),
|
||||
"gold",
|
||||
BigDecimal.valueOf(1000.0),
|
||||
"palladium",
|
||||
BigDecimal.valueOf(15000)))
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build()));
|
||||
persistResource(newTld("foobar", "FOOBAR").asBuilder().setPremiumList(premiumList).build());
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "silver")).hasValue(moneyOf(JPY, 10));
|
||||
assertThat(PremiumListDao.getPremiumPrice("premlist", "gold")).hasValue(moneyOf(JPY, 1000));
|
||||
@@ -262,14 +274,18 @@ public class PremiumListDaoTest {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> PremiumListDao.save("test-list", CurrencyUnit.GBP, ImmutableList.of()));
|
||||
() ->
|
||||
tm().transact(
|
||||
() ->
|
||||
PremiumListDao.save(
|
||||
"test-list", CurrencyUnit.GBP, ImmutableList.of())));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("New premium list data cannot be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_savePremiumList_clearsCache() {
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname")).isNull();
|
||||
PremiumListDao.save(testList);
|
||||
tm().transact(() -> PremiumListDao.save(testList));
|
||||
PremiumList pl = PremiumListDao.getLatestRevision("testname").get();
|
||||
assertThat(PremiumListDao.premiumListCache.getIfPresent("testname")).hasValue(pl);
|
||||
tm().transact(() -> PremiumListDao.save("testname", USD, ImmutableList.of("test,USD 1")));
|
||||
@@ -288,7 +304,7 @@ public class PremiumListDaoTest {
|
||||
.setLabelsToPrices(prices)
|
||||
.setCreationTimestamp(fakeClock.nowUtc())
|
||||
.build();
|
||||
PremiumListDao.save(list);
|
||||
tm().transact(() -> PremiumListDao.save(list));
|
||||
long duration = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS);
|
||||
if (duration >= 6000) {
|
||||
// Don't fail directly since we can't rely on what sort of machines the test is running on
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model.tld.label;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistReservedList;
|
||||
@@ -101,7 +102,8 @@ public class PremiumListTest {
|
||||
|
||||
@Test
|
||||
void testParse_canIncludeOrNotIncludeCurrencyUnit() {
|
||||
PremiumListDao.save("tld", USD, ImmutableList.of("rofl,USD 90", "paper, 80"));
|
||||
tm().transact(
|
||||
() -> PremiumListDao.save("tld", USD, ImmutableList.of("rofl,USD 90", "paper, 80")));
|
||||
assertThat(PremiumListDao.getPremiumPrice("tld", "rofl")).hasValue(Money.of(USD, 90));
|
||||
assertThat(PremiumListDao.getPremiumPrice("tld", "paper")).hasValue(Money.of(USD, 80));
|
||||
}
|
||||
|
||||
@@ -21,16 +21,22 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.math.BigDecimal;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link PremiumListUtils}. */
|
||||
class PremiumListUtilsTest {
|
||||
|
||||
private static final DateTime SAMPLE_TIME = DateTime.parse("2026-01-26T21:06:12.284Z");
|
||||
|
||||
@Test
|
||||
void parseInputToPremiumList_works() {
|
||||
PremiumList premiumList =
|
||||
parseToPremiumList(
|
||||
"testlist", USD, ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,USD 10"));
|
||||
"testlist",
|
||||
USD,
|
||||
ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,USD 10"),
|
||||
SAMPLE_TIME);
|
||||
assertThat(premiumList.getName()).isEqualTo("testlist");
|
||||
assertThat(premiumList.getLabelsToPrices())
|
||||
.containsExactly("foo", twoDigits(99.50), "bar", twoDigits(30), "baz", twoDigits(10));
|
||||
@@ -45,7 +51,8 @@ class PremiumListUtilsTest {
|
||||
parseToPremiumList(
|
||||
"testlist",
|
||||
USD,
|
||||
ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,JPY 990")));
|
||||
ImmutableList.of("foo,USD 99.50", "bar,USD 30", "baz,JPY 990"),
|
||||
SAMPLE_TIME));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("The currency unit must be USD");
|
||||
}
|
||||
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
// Copyright 2026 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.transaction;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link DelegatingReplicaJpaTransactionManager}. */
|
||||
public class DelegatingReplicaJpaTransactionManagerTest {
|
||||
|
||||
private JpaTransactionManager replica1 = mock(JpaTransactionManager.class);
|
||||
private JpaTransactionManager replica2 = mock(JpaTransactionManager.class);
|
||||
private Random random = mock(Random.class);
|
||||
private DelegatingReplicaJpaTransactionManager transactionManager =
|
||||
new DelegatingReplicaJpaTransactionManager(ImmutableList.of(replica1, replica2), random);
|
||||
|
||||
@Test
|
||||
void testGetReplica_rotates() {
|
||||
when(random.nextInt(2)).thenReturn(0).thenReturn(1);
|
||||
|
||||
transactionManager.loadByKey(null);
|
||||
verify(replica1).loadByKey(null);
|
||||
|
||||
transactionManager.loadByKey(null);
|
||||
verify(replica2).loadByKey(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransact_usesSameReplica() throws Exception {
|
||||
when(random.nextInt(2)).thenReturn(1);
|
||||
when(replica2.transact(any(), any(), anyBoolean()))
|
||||
.thenAnswer(
|
||||
invocation -> {
|
||||
Callable<Object> work = invocation.getArgument(1);
|
||||
return work.call();
|
||||
});
|
||||
|
||||
transactionManager.transact(
|
||||
() -> {
|
||||
transactionManager.loadByKey(null);
|
||||
return null;
|
||||
});
|
||||
|
||||
verify(replica2).transact(any(), any(), anyBoolean());
|
||||
// The loadByKey inside the transact should also use replica2.
|
||||
verify(replica2).loadByKey(null);
|
||||
// And it should NOT have called random again for the nested call.
|
||||
verify(random).nextInt(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTransactNoRetry_usesSameReplica() throws Exception {
|
||||
when(random.nextInt(2)).thenReturn(0);
|
||||
when(replica1.transactNoRetry(any(), any(), anyBoolean()))
|
||||
.thenAnswer(
|
||||
invocation -> {
|
||||
Callable<Object> work = invocation.getArgument(1);
|
||||
return work.call();
|
||||
});
|
||||
|
||||
transactionManager.transactNoRetry(
|
||||
() -> {
|
||||
transactionManager.loadByKey(null);
|
||||
return null;
|
||||
});
|
||||
|
||||
verify(replica1).transactNoRetry(any(), any(), anyBoolean());
|
||||
verify(replica1).loadByKey(null);
|
||||
verify(random).nextInt(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReTransactNoRetry_usesSameReplica() throws Exception {
|
||||
when(random.nextInt(2)).thenReturn(0);
|
||||
when(replica1.reTransact(any(Callable.class)))
|
||||
.thenAnswer(
|
||||
invocation -> {
|
||||
Callable<Object> work = invocation.getArgument(0);
|
||||
return work.call();
|
||||
});
|
||||
|
||||
transactionManager.reTransact(
|
||||
() -> {
|
||||
transactionManager.loadByKey(null);
|
||||
return null;
|
||||
});
|
||||
|
||||
verify(replica1).reTransact(any(Callable.class));
|
||||
verify(replica1).loadByKey(null);
|
||||
verify(random).nextInt(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInTransaction() {
|
||||
when(random.nextInt(2)).thenReturn(0);
|
||||
when(replica1.inTransaction()).thenReturn(true);
|
||||
|
||||
// Not in transaction yet
|
||||
assertThat(transactionManager.inTransaction()).isFalse();
|
||||
|
||||
transactionManager.transact(
|
||||
() -> {
|
||||
assertThat(transactionManager.inTransaction()).isTrue();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTeardown_tearsDownAllReplicas() {
|
||||
transactionManager.teardown();
|
||||
verify(replica1).teardown();
|
||||
verify(replica2).teardown();
|
||||
}
|
||||
}
|
||||
@@ -31,15 +31,18 @@ import google.registry.bigquery.BigqueryConnection.DestinationTable;
|
||||
import google.registry.bigquery.BigqueryUtils.TableType;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
import google.registry.reporting.icann.IcannReportingModule.ReportType;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.YearMonth;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link google.registry.reporting.icann.IcannReportingStager}. */
|
||||
class IcannReportingStagerTest {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2026-01-26T21:06:12.284Z"));
|
||||
private BigqueryConnection bigquery = mock(BigqueryConnection.class);
|
||||
FakeResponse response = new FakeResponse();
|
||||
private YearMonth yearMonth = new YearMonth(2017, 6);
|
||||
@@ -63,7 +66,7 @@ class IcannReportingStagerTest {
|
||||
when(bigquery.startQuery(any(String.class), any(DestinationTable.class)))
|
||||
.thenReturn(fakeFuture());
|
||||
DestinationTable.Builder tableBuilder =
|
||||
new DestinationTable.Builder()
|
||||
new DestinationTable.Builder(clock)
|
||||
.datasetId("testdataset")
|
||||
.type(TableType.TABLE)
|
||||
.name("tablename")
|
||||
|
||||
@@ -346,7 +346,7 @@ public final class DatabaseHelper {
|
||||
// increasing sequence, if we don't pad out the ID here, we would have to renumber hundreds of
|
||||
// unit tests.
|
||||
tm().reTransact(tm()::allocateId);
|
||||
PremiumListDao.save(premiumList);
|
||||
tm().transact(() -> PremiumListDao.save(premiumList));
|
||||
maybeAdvanceClock();
|
||||
return premiumList;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.keyring.api.PgpHelper.KeyRequirement.SIGN;
|
||||
import static google.registry.testing.TestDataHelper.loadBytes;
|
||||
import static google.registry.testing.TestDataHelper.loadFile;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.io.ByteSource;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -57,7 +58,8 @@ public final class FakeKeyringModule {
|
||||
private static final String MARKSDB_SMDRL_LOGIN_AND_PASSWORD = "smdrl:yolo";
|
||||
private static final String BSA_API_KEY = "bsaapikey";
|
||||
private static final String SQL_PRIMARY_CONNECTION = "project:primary-region:primary-name";
|
||||
private static final String SQL_REPLICA_CONNECTION = "project:replica-region:replica-name";
|
||||
private static final String SQL_REPLICA_CONNECTION_1 = "project:replica-region:replica-name";
|
||||
private static final String SQL_REPLICA_CONNECTION_2 = "project:replica-region:replica-name-2";
|
||||
|
||||
@Provides
|
||||
public Keyring get() {
|
||||
@@ -160,7 +162,12 @@ public final class FakeKeyringModule {
|
||||
|
||||
@Override
|
||||
public String getSqlReplicaConnectionName() {
|
||||
return SQL_REPLICA_CONNECTION;
|
||||
return SQL_REPLICA_CONNECTION_1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<String> getSqlReplicaConnectionNames() {
|
||||
return ImmutableList.of(SQL_REPLICA_CONNECTION_1, SQL_REPLICA_CONNECTION_2);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,7 +30,6 @@ import google.registry.model.poll.PollMessage.OneTime;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -38,13 +37,12 @@ import org.junit.jupiter.api.Test;
|
||||
/** Unit tests for {@link AckPollMessagesCommand}. */
|
||||
public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesCommand> {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2015-02-04T08:16:32.064Z"));
|
||||
|
||||
private DomainHistory domainHistory;
|
||||
|
||||
@BeforeEach
|
||||
final void beforeEach() {
|
||||
command.clock = clock;
|
||||
command.clock = fakeClock;
|
||||
fakeClock.setTo(DateTime.parse("2015-02-04T08:16:32.064Z"));
|
||||
createTld("tld");
|
||||
Domain domain =
|
||||
DatabaseHelper.newDomain("example.tld").asBuilder().setRepoId("FSDGS-TLD").build();
|
||||
@@ -52,12 +50,12 @@ public class AckPollMessagesCommandTest extends CommandTestCase<AckPollMessagesC
|
||||
domainHistory =
|
||||
persistResource(
|
||||
new DomainHistory.Builder()
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setModificationTime(fakeClock.nowUtc())
|
||||
.setDomain(domain)
|
||||
.setRegistrarId(domain.getCreationRegistrarId())
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
fakeClock.advanceOneMilli();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -68,10 +68,10 @@ public class ConfigureTldCommandTest extends CommandTestCase<ConfigureTldCommand
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
command.mapper = objectMapper;
|
||||
premiumList = persistPremiumList("test", USD, "silver,USD 50", "gold,USD 80");
|
||||
command.validDnsWriterNames = ImmutableSet.of("VoidDnsWriter", "FooDnsWriter");
|
||||
command.clock = fakeClock;
|
||||
logger.addHandler(logHandler);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ class CreateAnchorTenantCommandTest extends EppToolCommandTestCase<CreateAnchorT
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
command.passwordGenerator = new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
command.passwordGenerator = new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||
command.printStream = System.out;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ abstract class CreateOrUpdatePremiumListCommandTestCase<T extends CreateOrUpdate
|
||||
|
||||
@BeforeEach
|
||||
void beforeEachCreateOrUpdatePremiumListCommandTestCase() throws IOException {
|
||||
command.clock = fakeClock;
|
||||
// initial set up for both CreatePremiumListCommand and UpdatePremiumListCommand test cases;
|
||||
initialPremiumListData = "doge,USD 9090";
|
||||
File premiumTermsFile = tmpDir.resolve(TLD_TEST + ".txt").toFile();
|
||||
|
||||
+1
@@ -44,6 +44,7 @@ abstract class CreateOrUpdateReservedListCommandTestCase<
|
||||
|
||||
@BeforeEach
|
||||
void beforeEachCreateOrUpdateReservedListCommandTestCase() throws IOException {
|
||||
command.clock = fakeClock;
|
||||
File reservedTermsFile = tmpDir.resolve("xn--q9jyb4c_common-reserved.txt").toFile();
|
||||
File invalidReservedTermsFile = tmpDir.resolve("reserved-terms-wontparse.csv").toFile();
|
||||
String reservedTermsCsv =
|
||||
|
||||
@@ -54,7 +54,6 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||
// since the old entity is always null and file cannot be empty, the prompt will NOT be "No entity
|
||||
// changes to apply."
|
||||
void commandPrompt_successStageNewEntity() throws Exception {
|
||||
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||
command.inputFile = Paths.get(premiumTermsPath);
|
||||
command.currencyUnit = "USD";
|
||||
command.prompt();
|
||||
@@ -63,7 +62,6 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_successStageNewEntityWithOverride() throws Exception {
|
||||
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||
String alterTld = "override";
|
||||
command.inputFile = Paths.get(premiumTermsPath);
|
||||
command.override = true;
|
||||
@@ -75,7 +73,6 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureNoInputFile() {
|
||||
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||
assertThrows(NullPointerException.class, command::prompt);
|
||||
}
|
||||
|
||||
@@ -83,7 +80,6 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||
void commandPrompt_failurePremiumListAlreadyExists() {
|
||||
String randomStr = "random";
|
||||
DatabaseHelper.createTld(randomStr);
|
||||
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||
command.name = randomStr;
|
||||
command.currencyUnit = "USD";
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
@@ -92,7 +88,6 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureMismatchedTldFileName_noOverride() throws Exception {
|
||||
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||
String fileName = "random";
|
||||
Path tmpPath = tmpDir.resolve(String.format("%s.txt", fileName));
|
||||
Files.write(new byte[0], tmpPath.toFile());
|
||||
@@ -111,7 +106,6 @@ class CreatePremiumListCommandTest<C extends CreatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureMismatchedTldName_noOverride() {
|
||||
CreatePremiumListCommand command = new CreatePremiumListCommand();
|
||||
String fileName = "random";
|
||||
command.name = fileName;
|
||||
command.currencyUnit = "USD";
|
||||
|
||||
@@ -58,6 +58,7 @@ class CreateRegistrarCommandTest extends CommandTestCase<CreateRegistrarCommand>
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
command.setConnection(connection);
|
||||
command.certificateChecker =
|
||||
new CertificateChecker(
|
||||
|
||||
@@ -36,6 +36,7 @@ class CreateReservedListCommandTest
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
createTlds("xn--q9jyb4c", "soy");
|
||||
}
|
||||
|
||||
@@ -162,7 +163,6 @@ class CreateReservedListCommandTest
|
||||
|
||||
@Test
|
||||
void testStageEntityChange_succeeds() throws Exception {
|
||||
CreateReservedListCommand command = new CreateReservedListCommand();
|
||||
// file content is populated in @BeforeEach of CreateOrUpdateReservedListCommandTestCase.java
|
||||
command.input = Paths.get(reservedTermsPath);
|
||||
command.init();
|
||||
@@ -176,7 +176,6 @@ class CreateReservedListCommandTest
|
||||
void testStageEntityChange_succeedsWithEmptyFile() throws Exception {
|
||||
Path tmpPath = tmpDir.resolve("xn--q9jyb4c_common-tmp.txt");
|
||||
Files.write(new byte[0], tmpPath.toFile());
|
||||
CreateReservedListCommand command = new CreateReservedListCommand();
|
||||
command.input = tmpPath;
|
||||
command.init();
|
||||
assertThat(command.prompt()).contains("reservedListMap=[]");
|
||||
|
||||
@@ -33,6 +33,7 @@ public abstract class EppToolCommandTestCase<C extends EppToolCommand> extends C
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEachEppToolCommandTestCase() {
|
||||
command.clock = fakeClock;
|
||||
// Create two TLDs for commands that allow multiple TLDs at once.
|
||||
createTlds("tld", "tld2");
|
||||
eppVerifier = EppToolVerifier.create(command).expectRegistrarId("NewRegistrar");
|
||||
|
||||
@@ -22,7 +22,6 @@ import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
@@ -36,13 +35,11 @@ import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import org.joda.time.DateTime;
|
||||
import org.json.simple.JSONValue;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -51,8 +48,6 @@ import org.junit.jupiter.api.Test;
|
||||
/** Unit tests for {@link GenerateDnsReportCommand}. */
|
||||
class GenerateDnsReportCommandTest extends CommandTestCase<GenerateDnsReportCommand> {
|
||||
|
||||
private final DateTime now = DateTime.now(UTC);
|
||||
private final FakeClock clock = new FakeClock();
|
||||
private Path output;
|
||||
|
||||
private Object getOutputAsJson() throws IOException, ParseException {
|
||||
@@ -117,8 +112,7 @@ class GenerateDnsReportCommandTest extends CommandTestCase<GenerateDnsReportComm
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
output = tmpDir.resolve("out.dat");
|
||||
command.clock = clock;
|
||||
clock.setTo(now);
|
||||
command.clock = fakeClock;
|
||||
|
||||
createTlds("xn--q9jyb4c", "example");
|
||||
nameserver1 =
|
||||
@@ -170,7 +164,7 @@ class GenerateDnsReportCommandTest extends CommandTestCase<GenerateDnsReportComm
|
||||
|
||||
@Test
|
||||
void testSuccess_skipDeletedDomain() throws Exception {
|
||||
persistResource(domain1.asBuilder().setDeletionTime(now).build());
|
||||
persistResource(domain1.asBuilder().setDeletionTime(fakeClock.nowUtc()).build());
|
||||
runCommand("--output=" + output, "--tld=xn--q9jyb4c");
|
||||
assertThat((Iterable<?>) getOutputAsJson())
|
||||
.containsExactly(DOMAIN2_OUTPUT, NAMESERVER1_OUTPUT, NAMESERVER2_OUTPUT);
|
||||
@@ -178,7 +172,7 @@ class GenerateDnsReportCommandTest extends CommandTestCase<GenerateDnsReportComm
|
||||
|
||||
@Test
|
||||
void testSuccess_skipDeletedNameserver() throws Exception {
|
||||
persistResource(nameserver1.asBuilder().setDeletionTime(now).build());
|
||||
persistResource(nameserver1.asBuilder().setDeletionTime(fakeClock.nowUtc()).build());
|
||||
runCommand("--output=" + output, "--tld=xn--q9jyb4c");
|
||||
Iterable<?> output = (Iterable<?>) getOutputAsJson();
|
||||
assertThat(output).containsAnyOf(DOMAIN1_OUTPUT, DOMAIN1_OUTPUT_ALT);
|
||||
@@ -207,7 +201,7 @@ class GenerateDnsReportCommandTest extends CommandTestCase<GenerateDnsReportComm
|
||||
domain1
|
||||
.asBuilder()
|
||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
||||
.setDeletionTime(now.plusDays(30))
|
||||
.setDeletionTime(fakeClock.nowUtc().plusDays(30))
|
||||
.build());
|
||||
runCommand("--output=" + output, "--tld=xn--q9jyb4c");
|
||||
assertThat((Iterable<?>) getOutputAsJson())
|
||||
|
||||
@@ -30,8 +30,8 @@ class GetDomainCommandTest extends CommandTestCase<GetDomainCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
command.clock = fakeClock;
|
||||
createTld("tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,7 +22,6 @@ import static google.registry.testing.FullFieldsTestEntityHelper.makeHistoryEntr
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -30,12 +29,12 @@ import org.junit.jupiter.api.Test;
|
||||
/** Unit tests for {@link GetClaimsListCommand}. */
|
||||
class GetHistoryEntriesCommandTest extends CommandTestCase<GetHistoryEntriesCommand> {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01T00:00:00Z"));
|
||||
|
||||
private Domain domain;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
fakeClock.setTo(DateTime.parse("2000-01-01T00:00:00Z"));
|
||||
command.clock = fakeClock;
|
||||
createTld("tld");
|
||||
domain = persistActiveDomain("example.tld");
|
||||
}
|
||||
@@ -48,18 +47,18 @@ class GetHistoryEntriesCommandTest extends CommandTestCase<GetHistoryEntriesComm
|
||||
HistoryEntry.Type.DOMAIN_CREATE,
|
||||
Period.create(1, Period.Unit.YEARS),
|
||||
"created",
|
||||
clock.nowUtc()));
|
||||
fakeClock.nowUtc()));
|
||||
runCommand("--id=example.tld", "--type=DOMAIN");
|
||||
assertStdoutIs(
|
||||
"""
|
||||
Client: TheRegistrar
|
||||
Time: 2000-01-01T00:00:00.000Z
|
||||
Client TRID: ABC-123
|
||||
Server TRID: server-trid
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<xml/>
|
||||
Client: TheRegistrar
|
||||
Time: 2000-01-01T00:00:00.000Z
|
||||
Client TRID: ABC-123
|
||||
Server TRID: server-trid
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<xml/>
|
||||
|
||||
""");
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,8 +69,8 @@ class GetHistoryEntriesCommandTest extends CommandTestCase<GetHistoryEntriesComm
|
||||
HistoryEntry.Type.DOMAIN_CREATE,
|
||||
Period.create(1, Period.Unit.YEARS),
|
||||
"created",
|
||||
clock.nowUtc()));
|
||||
runCommand("--before", clock.nowUtc().minusMinutes(1).toString());
|
||||
fakeClock.nowUtc()));
|
||||
runCommand("--before", fakeClock.nowUtc().minusMinutes(1).toString());
|
||||
assertStdoutIs("");
|
||||
}
|
||||
|
||||
@@ -83,8 +82,8 @@ class GetHistoryEntriesCommandTest extends CommandTestCase<GetHistoryEntriesComm
|
||||
HistoryEntry.Type.DOMAIN_CREATE,
|
||||
Period.create(1, Period.Unit.YEARS),
|
||||
"created",
|
||||
clock.nowUtc()));
|
||||
runCommand("--after", clock.nowUtc().plusMinutes(1).toString());
|
||||
fakeClock.nowUtc()));
|
||||
runCommand("--after", fakeClock.nowUtc().plusMinutes(1).toString());
|
||||
assertStdoutIs("");
|
||||
}
|
||||
|
||||
@@ -96,22 +95,22 @@ class GetHistoryEntriesCommandTest extends CommandTestCase<GetHistoryEntriesComm
|
||||
HistoryEntry.Type.DOMAIN_CREATE,
|
||||
Period.create(1, Period.Unit.YEARS),
|
||||
"created",
|
||||
clock.nowUtc()));
|
||||
fakeClock.nowUtc()));
|
||||
runCommand(
|
||||
"--after",
|
||||
clock.nowUtc().minusMinutes(1).toString(),
|
||||
fakeClock.nowUtc().minusMinutes(1).toString(),
|
||||
"--before",
|
||||
clock.nowUtc().plusMinutes(1).toString());
|
||||
fakeClock.nowUtc().plusMinutes(1).toString());
|
||||
assertStdoutIs(
|
||||
"""
|
||||
Client: TheRegistrar
|
||||
Time: 2000-01-01T00:00:00.000Z
|
||||
Client TRID: ABC-123
|
||||
Server TRID: server-trid
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<xml/>
|
||||
Client: TheRegistrar
|
||||
Time: 2000-01-01T00:00:00.000Z
|
||||
Client TRID: ABC-123
|
||||
Server TRID: server-trid
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<xml/>
|
||||
|
||||
""");
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -122,20 +121,20 @@ class GetHistoryEntriesCommandTest extends CommandTestCase<GetHistoryEntriesComm
|
||||
HistoryEntry.Type.DOMAIN_CREATE,
|
||||
Period.create(1, Period.Unit.YEARS),
|
||||
"created",
|
||||
clock.nowUtc())
|
||||
fakeClock.nowUtc())
|
||||
.asBuilder()
|
||||
.setTrid(null)
|
||||
.build());
|
||||
runCommand("--id=example.tld", "--type=DOMAIN");
|
||||
assertStdoutIs(
|
||||
"""
|
||||
Client: TheRegistrar
|
||||
Time: 2000-01-01T00:00:00.000Z
|
||||
Client TRID: null
|
||||
Server TRID: null
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<xml/>
|
||||
Client: TheRegistrar
|
||||
Time: 2000-01-01T00:00:00.000Z
|
||||
Client TRID: null
|
||||
Server TRID: null
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<xml/>
|
||||
|
||||
""");
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ class GetHostCommandTest extends CommandTestCase<GetHostCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
command.clock = fakeClock;
|
||||
createTld("tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -47,7 +47,6 @@ import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.util.Optional;
|
||||
@@ -70,8 +69,9 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
fakeClock.setTo(DateTime.parse("2018-07-07TZ"));
|
||||
command.clock = fakeClock;
|
||||
command.passwordGenerator = passwordGenerator;
|
||||
command.clock = new FakeClock(DateTime.parse("2018-07-07TZ"));
|
||||
command.maybeGroupEmailAddress = Optional.of("group@example.com");
|
||||
command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
command.iamClient = iamClient;
|
||||
|
||||
@@ -41,6 +41,7 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
registry = createTld(TLD_TEST, USD, initialPremiumListData);
|
||||
}
|
||||
|
||||
@@ -60,7 +61,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
|
||||
String newPremiumListData = "omg,USD 1234";
|
||||
Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.inputFile = Paths.get(tmpFile.getPath());
|
||||
command.name = TLD_TEST;
|
||||
assertThat(command.prompt()).contains("Update premium list for prime?");
|
||||
@@ -69,7 +69,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
@Test
|
||||
void commandPrompt_successStageNoChange() throws Exception {
|
||||
File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.inputFile = Paths.get(tmpFile.getPath());
|
||||
command.name = TLD_TEST;
|
||||
assertThat(command.prompt())
|
||||
@@ -82,7 +81,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
String newPremiumListData = "eth,USD 9999";
|
||||
Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
|
||||
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
// data come from @beforeEach of CreateOrUpdatePremiumListCommandTestCase.java
|
||||
command.inputFile = Paths.get(tmpFile.getPath());
|
||||
runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile);
|
||||
@@ -96,7 +94,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
void commandRun_successNoChange() throws Exception {
|
||||
File tmpFile = tmpDir.resolve(String.format("%s.txt", TLD_TEST)).toFile();
|
||||
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.inputFile = Paths.get(tmpFile.getPath());
|
||||
runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile);
|
||||
|
||||
@@ -113,7 +110,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
String newPremiumListData = "eth,USD 9999";
|
||||
Files.asCharSink(newPremiumFile, UTF_8).write(newPremiumListData);
|
||||
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
// data come from @beforeEach of CreateOrUpdatePremiumListCommandTestCase.java
|
||||
command.inputFile = Paths.get(newPremiumFile.getPath());
|
||||
runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile);
|
||||
@@ -129,7 +125,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
String premiumTerms = "foo,USD 9000\ndoge,USD 100\nelon,USD 2021";
|
||||
Files.asCharSink(tmpFile, UTF_8).write(premiumTerms);
|
||||
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.inputFile = Paths.get(tmpFile.getPath());
|
||||
runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile);
|
||||
|
||||
@@ -146,7 +141,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
Path tmpPath = tmpDir.resolve(String.format("%s.txt", TLD_TEST));
|
||||
Files.write(new byte[0], tmpPath.toFile());
|
||||
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.inputFile = tmpPath;
|
||||
command.name = TLD_TEST;
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
@@ -156,7 +150,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
@Test
|
||||
void commandPrompt_failureNoPreviousVersion() {
|
||||
registry = createTld("random", null, null);
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.name = "random";
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
assertThat(thrown)
|
||||
@@ -166,13 +159,11 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureNoInputFile() {
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
assertThrows(NullPointerException.class, command::prompt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureTldFromNameDoesNotExist() {
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.name = "random2";
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
assertThat(thrown)
|
||||
@@ -182,7 +173,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureTldFromInputFileDoesNotExist() {
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
// using tld extracted from file name but this tld is not part of the registry
|
||||
command.inputFile = Paths.get(tmpDir.resolve("random3.txt").toFile().getPath());
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
@@ -197,7 +187,6 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
String newPremiumListData = "eth,USD 9999";
|
||||
Files.asCharSink(tmpFile, UTF_8).write(newPremiumListData);
|
||||
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.inputFile = Paths.get(tmpFile.getPath());
|
||||
runCommandForced("--name=" + TLD_TEST, "--input=" + command.inputFile, "--dry_run");
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ class UpdateRegistrarCommandTest extends CommandTestCase<UpdateRegistrarCommand>
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
command.certificateChecker =
|
||||
new CertificateChecker(
|
||||
ImmutableSortedMap.of(START_OF_TIME, 825, DateTime.parse("2020-09-01T00:00:00Z"), 398),
|
||||
|
||||
@@ -98,7 +98,6 @@ class UpdateReservedListCommandTest
|
||||
Files.asCharSink(reservedTermsFile, UTF_8).write(reservedTermsCsv);
|
||||
reservedTermsPath = reservedTermsFile.getPath();
|
||||
// create a command instance and assign its input
|
||||
UpdateReservedListCommand command = new UpdateReservedListCommand();
|
||||
command.input = Paths.get(reservedTermsPath);
|
||||
// run again with terms from example_reserved_terms.csv
|
||||
command.init();
|
||||
@@ -110,7 +109,6 @@ class UpdateReservedListCommandTest
|
||||
void testSuccess_withChanges() throws Exception {
|
||||
// changes come from example_reserved_terms.csv, which are populated in @BeforeEach of
|
||||
// CreateOrUpdateReservedListCommandTestCases.java
|
||||
UpdateReservedListCommand command = new UpdateReservedListCommand();
|
||||
command.input = Paths.get(reservedTermsPath);
|
||||
command.init();
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ class ValidateLoginCredentialsCommandTest extends CommandTestCase<ValidateLoginC
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.clock = fakeClock;
|
||||
createTld("tld");
|
||||
persistResource(
|
||||
loadRegistrar("NewRegistrar")
|
||||
|
||||
+3
-2
@@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Provider;
|
||||
@@ -156,9 +157,9 @@ public final class CertificateSupplierModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
static SelfSignedCaCertificate provideSelfSignedCertificate() {
|
||||
static SelfSignedCaCertificate provideSelfSignedCertificate(Clock clock) {
|
||||
try {
|
||||
return SelfSignedCaCertificate.create();
|
||||
return SelfSignedCaCertificate.create(clock);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
+8
-7
@@ -22,6 +22,7 @@ import static google.registry.networking.handler.SslInitializerTestUtils.verifyS
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
@@ -173,7 +174,7 @@ class SslClientInitializerTest {
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_defaultTrustManager_rejectSelfSignedCert(SslProvider sslProvider)
|
||||
throws Exception {
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress =
|
||||
new LocalAddress("DEFAULT_TRUST_MANAGER_REJECT_SELF_SIGNED_CERT_" + sslProvider);
|
||||
nettyExtension.setUpServer(localAddress, getServerHandler(false, ssc.key(), ssc.cert()));
|
||||
@@ -204,7 +205,7 @@ class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self-signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
X509Certificate cert = signKeyPair(ssc, keyPair, SSL_HOST);
|
||||
|
||||
// Set up the server to use the signed cert and private key to perform handshake;
|
||||
@@ -239,7 +240,7 @@ class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self-signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
X509Certificate cert =
|
||||
signKeyPair(
|
||||
ssc, keyPair, SSL_HOST, DateTime.now(UTC).minusDays(2), DateTime.now(UTC).minusDays(1));
|
||||
@@ -276,7 +277,7 @@ class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self-signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
X509Certificate cert =
|
||||
signKeyPair(
|
||||
ssc, keyPair, SSL_HOST, DateTime.now(UTC).plusDays(1), DateTime.now(UTC).plusDays(2));
|
||||
@@ -310,8 +311,8 @@ class SslClientInitializerTest {
|
||||
new LocalAddress(
|
||||
"CUSTOM_TRUST_MANAGER_ACCEPT_SELF_SIGNED_CERT_CLIENT_CERT_REQUIRED_" + sslProvider);
|
||||
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
|
||||
// Set up the server to require client certificate.
|
||||
nettyExtension.setUpServer(
|
||||
@@ -352,7 +353,7 @@ class SslClientInitializerTest {
|
||||
KeyPair keyPair = getKeyPair();
|
||||
|
||||
// Generate a self-signed certificate, and use it to sign the key pair.
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
X509Certificate cert = signKeyPair(ssc, keyPair, "wrong.com");
|
||||
|
||||
// Set up the server to use the signed cert and private key to perform handshake;
|
||||
|
||||
+16
-14
@@ -24,6 +24,7 @@ import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
@@ -148,7 +149,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_swappedInitializerWithSslHandler(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate ssc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
SslServerInitializer<EmbeddedChannel> sslServerInitializer =
|
||||
new SslServerInitializer<>(
|
||||
true,
|
||||
@@ -169,13 +170,13 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_trustAnyClientCert(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("TRUST_ANY_CLIENT_CERT_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
localAddress,
|
||||
getServerHandler(true, false, sslProvider, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
|
||||
@@ -193,7 +194,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_cipherNotAccepted(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("CIPHER_NOT_ACCEPTED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
@@ -220,7 +221,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_someCiphersNotAccepted(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("SOME_CIPHERS_NOT_ACCEPTED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
@@ -258,7 +259,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_protocolNotAccepted(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("PROTOCOL_NOT_ACCEPTED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
@@ -288,7 +289,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_clientCertExpired(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
@@ -309,7 +310,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_clientCertNotYetValid(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("CLIENT_CERT_EXPIRED_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
@@ -330,7 +331,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_doesNotRequireClientCert(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("DOES_NOT_REQUIRE_CLIENT_CERT_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
@@ -353,7 +354,7 @@ class SslServerInitializerTest {
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testSuccess_CertSignedByOtherCa(SslProvider sslProvider) throws Exception {
|
||||
// The self-signed cert of the CA.
|
||||
SelfSignedCaCertificate caSsc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate caSsc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
KeyPair keyPair = getKeyPair();
|
||||
X509Certificate serverCert = signKeyPair(caSsc, keyPair, SSL_HOST);
|
||||
LocalAddress localAddress = new LocalAddress("CERT_SIGNED_BY_OTHER_CA_" + sslProvider);
|
||||
@@ -368,7 +369,7 @@ class SslServerInitializerTest {
|
||||
// Serving both the server cert, and the CA cert
|
||||
serverCert,
|
||||
caSsc.cert()));
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(
|
||||
@@ -392,7 +393,7 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_requireClientCertificate(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST);
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create(SSL_HOST, new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("REQUIRE_CLIENT_CERT_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
@@ -417,13 +418,14 @@ class SslServerInitializerTest {
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestCombinations")
|
||||
void testFailure_wrongHostnameInCertificate(SslProvider sslProvider) throws Exception {
|
||||
SelfSignedCaCertificate serverSsc = SelfSignedCaCertificate.create("wrong.com");
|
||||
SelfSignedCaCertificate serverSsc =
|
||||
SelfSignedCaCertificate.create("wrong.com", new FakeClock());
|
||||
LocalAddress localAddress = new LocalAddress("WRONG_HOSTNAME_" + sslProvider);
|
||||
|
||||
nettyExtension.setUpServer(
|
||||
localAddress,
|
||||
getServerHandler(true, false, sslProvider, serverSsc.key(), serverSsc.cert()));
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create();
|
||||
SelfSignedCaCertificate clientSsc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
nettyExtension.setUpClient(
|
||||
localAddress,
|
||||
getClientHandler(sslProvider, serverSsc.cert(), clientSsc.key(), clientSsc.cert()));
|
||||
|
||||
+8
-1
@@ -26,6 +26,8 @@ import dagger.Component;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.networking.module.CertificateSupplierModule.Mode;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.inject.Singleton;
|
||||
@@ -59,7 +61,7 @@ class CertificateSupplierModuleTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
ssc = SelfSignedCaCertificate.create();
|
||||
ssc = SelfSignedCaCertificate.create(new FakeClock());
|
||||
KeyPair keyPair = getKeyPair();
|
||||
key = keyPair.getPrivate();
|
||||
cert = signKeyPair(ssc, keyPair, "example.tld");
|
||||
@@ -147,6 +149,11 @@ class CertificateSupplierModuleTest {
|
||||
// Make the supplier always return the save value for test to save time.
|
||||
return Duration.ofDays(1);
|
||||
}
|
||||
|
||||
@Provides
|
||||
Clock provideClock() {
|
||||
return new FakeClock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import google.registry.monitoring.blackbox.exception.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.message.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.message.OutboundMessageType;
|
||||
import google.registry.util.Clock;
|
||||
import io.netty.channel.Channel;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
@@ -33,6 +34,7 @@ public abstract class EppToken extends Token {
|
||||
private static AtomicInteger clientIdSuffix = new AtomicInteger();
|
||||
|
||||
protected final String tld;
|
||||
protected final Clock clock;
|
||||
private String host;
|
||||
private String currentDomainName;
|
||||
|
||||
@@ -40,15 +42,16 @@ public abstract class EppToken extends Token {
|
||||
* Always the constructor used to provide any {@link EppToken}, with {@code tld} and {@code host}
|
||||
* specified by Dagger.
|
||||
*/
|
||||
protected EppToken(String tld, String host) {
|
||||
protected EppToken(String tld, String host, Clock clock) {
|
||||
this.tld = tld;
|
||||
this.host = host;
|
||||
this.clock = clock;
|
||||
currentDomainName = newDomainName(getNewTRID());
|
||||
}
|
||||
|
||||
/** Constructor used when passing on same {@link Channel} to next {@link Token}. */
|
||||
protected EppToken(String tld, String host, Channel channel) {
|
||||
this(tld, host);
|
||||
protected EppToken(String tld, String host, Clock clock, Channel channel) {
|
||||
this(tld, host, clock);
|
||||
setChannel(channel);
|
||||
}
|
||||
|
||||
@@ -79,7 +82,7 @@ public abstract class EppToken extends Token {
|
||||
private String getNewTRID() {
|
||||
return String.format(
|
||||
"prober-%s-%d-%d",
|
||||
"localhost", System.currentTimeMillis(), clientIdSuffix.incrementAndGet());
|
||||
"localhost", clock.nowUtc().getMillis(), clientIdSuffix.incrementAndGet());
|
||||
}
|
||||
|
||||
/** Return a fully qualified domain label to use, derived from the client transaction ID. */
|
||||
@@ -103,13 +106,13 @@ public abstract class EppToken extends Token {
|
||||
public static class Transient extends EppToken {
|
||||
|
||||
@Inject
|
||||
public Transient(@Named("eppTld") String tld, @Named("eppHost") String host) {
|
||||
super(tld, host);
|
||||
public Transient(@Named("eppTld") String tld, @Named("eppHost") String host, Clock clock) {
|
||||
super(tld, host, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token next() {
|
||||
return new Transient(tld, host());
|
||||
return new Transient(tld, host(), clock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,18 +124,18 @@ public abstract class EppToken extends Token {
|
||||
public static class Persistent extends EppToken {
|
||||
|
||||
@Inject
|
||||
public Persistent(@Named("eppTld") String tld, @Named("eppHost") String host) {
|
||||
super(tld, host);
|
||||
public Persistent(@Named("eppTld") String tld, @Named("eppHost") String host, Clock clock) {
|
||||
super(tld, host, clock);
|
||||
}
|
||||
|
||||
/** Constructor used on call to {@code next} to preserve channel. */
|
||||
private Persistent(String tld, String host, Channel channel) {
|
||||
super(tld, host, channel);
|
||||
private Persistent(String tld, String host, Clock clock, Channel channel) {
|
||||
super(tld, host, clock, channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token next() {
|
||||
return new Persistent(tld, host(), channel());
|
||||
return new Persistent(tld, host(), clock, channel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import google.registry.monitoring.blackbox.exception.UndeterminedStateException;
|
||||
import google.registry.monitoring.blackbox.message.EppRequestMessage;
|
||||
import google.registry.monitoring.blackbox.util.EppUtils;
|
||||
import google.registry.testing.FakeClock;
|
||||
import io.netty.channel.Channel;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
@@ -28,9 +29,10 @@ class EppTokenTest {
|
||||
|
||||
private static String TEST_HOST = "host";
|
||||
private static String TEST_TLD = "tld";
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
private EppToken persistentEppToken = new EppToken.Persistent(TEST_TLD, TEST_HOST);
|
||||
private EppToken transientEppToken = new EppToken.Transient(TEST_TLD, TEST_HOST);
|
||||
private EppToken persistentEppToken = new EppToken.Persistent(TEST_TLD, TEST_HOST, fakeClock);
|
||||
private EppToken transientEppToken = new EppToken.Transient(TEST_TLD, TEST_HOST, fakeClock);
|
||||
|
||||
@Test
|
||||
void testMessageModificationSuccess_PersistentToken() throws UndeterminedStateException {
|
||||
|
||||
@@ -24,6 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@@ -49,11 +50,11 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||
|
||||
private static final byte[] HELLO_BYTES =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<hello/>
|
||||
</epp>
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<hello/>
|
||||
</epp>
|
||||
"""
|
||||
.getBytes(UTF_8);
|
||||
|
||||
private X509Certificate certificate;
|
||||
@@ -122,7 +123,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||
@Override
|
||||
void beforeEach() throws Exception {
|
||||
testComponent = makeTestComponent();
|
||||
certificate = SelfSignedCaCertificate.create().cert();
|
||||
certificate = SelfSignedCaCertificate.create(new FakeClock()).cert();
|
||||
initializeChannel(
|
||||
ch -> {
|
||||
ch.attr(REMOTE_ADDRESS_KEY).set(CLIENT_ADDRESS);
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.google.common.io.BaseEncoding;
|
||||
import google.registry.proxy.TestUtils;
|
||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||
import google.registry.proxy.metric.FrontendMetrics;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.ProxyHttpHeaders;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@@ -114,7 +115,7 @@ class EppServiceHandlerTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
clientCertificate = SelfSignedCaCertificate.create().cert();
|
||||
clientCertificate = SelfSignedCaCertificate.create(new FakeClock()).cert();
|
||||
channel = setUpNewChannel(eppServiceHandler);
|
||||
}
|
||||
|
||||
@@ -171,7 +172,7 @@ class EppServiceHandlerTest {
|
||||
new EppServiceHandler(
|
||||
RELAY_HOST, RELAY_PATH, false, () -> ID_TOKEN, HELLO.getBytes(UTF_8), metrics);
|
||||
EmbeddedChannel channel2 = setUpNewChannel(eppServiceHandler2);
|
||||
X509Certificate clientCertificate2 = SelfSignedCaCertificate.create().cert();
|
||||
X509Certificate clientCertificate2 = SelfSignedCaCertificate.create(new FakeClock()).cert();
|
||||
setHandshakeSuccess(channel2, clientCertificate2);
|
||||
String certHash2 = getCertificateHash(clientCertificate2);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ steps:
|
||||
export JAVAC_EXTRACTOR_JAR="$${PWD}/kythe/extractors/javac_extractor.jar"
|
||||
jvmopts="--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED"
|
||||
jvmopts="$${jvmopts} --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
|
||||
jvmopts="$${jvmopts} --add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED"
|
||||
jvmopts="$${jvmopts} --add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED"
|
||||
jvmopts="$${jvmopts} --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"
|
||||
jvmopts="$${jvmopts} --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED"
|
||||
|
||||
@@ -27,7 +27,7 @@ steps:
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
# Set home for Gradle caches. Must be consistent with last step below
|
||||
# and ./build_nomulus_for_env.sh
|
||||
env: [ 'GRADLE_USER_HOME=/workspace/cloudbuild-caches' ]
|
||||
env: [ 'GRADLE_USER_HOME=/workspace/cloudbuild-caches', 'DOCKER_API_VERSION=1.41' ]
|
||||
entrypoint: /bin/bash
|
||||
args:
|
||||
- -c
|
||||
@@ -45,7 +45,7 @@ steps:
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
# Set home for Gradle caches. Must be consistent with last step below
|
||||
# and ./build_nomulus_for_env.sh
|
||||
env: [ 'GRADLE_USER_HOME=/workspace/cloudbuild-caches' ]
|
||||
env: [ 'GRADLE_USER_HOME=/workspace/cloudbuild-caches', 'DOCKER_API_VERSION=1.41' ]
|
||||
entrypoint: /bin/bash
|
||||
args:
|
||||
- -c
|
||||
@@ -102,6 +102,7 @@ steps:
|
||||
# nomulus.jar built earlier.
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
entrypoint: /bin/bash
|
||||
env: [ 'DOCKER_API_VERSION=1.41' ]
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
@@ -119,6 +120,7 @@ steps:
|
||||
# nomulus.jar built earlier.
|
||||
- name: 'gcr.io/${PROJECT_ID}/builder:latest'
|
||||
entrypoint: /bin/bash
|
||||
env: [ 'DOCKER_API_VERSION=1.41' ]
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
|
||||
@@ -78,7 +78,7 @@ if ! pgrep cloud_sql_proxy; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
/flyway/flyway -postgresql.transactional.lock=false -community \
|
||||
/flyway/flyway -postgresql.transactional.lock=false \
|
||||
-user=${db_user} -password=${db_password} \
|
||||
-url=jdbc:postgresql://localhost:5432/postgres \
|
||||
-locations=classpath:sql/flyway \
|
||||
@@ -87,7 +87,7 @@ migration_result=$?
|
||||
|
||||
if [ ${flyway_action} == "migrate" ]; then
|
||||
# After deployment, log the current schema.
|
||||
/flyway/flyway -community -user=${db_user} -password=${db_password} \
|
||||
/flyway/flyway -user=${db_user} -password=${db_password} \
|
||||
-url=jdbc:postgresql://localhost:5432/postgres \
|
||||
-locations=classpath:sql/flyway \
|
||||
info
|
||||
|
||||
@@ -324,12 +324,12 @@ public final class PosixTarHeader {
|
||||
private final byte[] header = new byte[HEADER_LENGTH];
|
||||
private boolean hasName = false;
|
||||
private boolean hasSize = false;
|
||||
private boolean hasMtime = false;
|
||||
|
||||
public Builder() {
|
||||
setMode(DEFAULT_MODE);
|
||||
setUid(DEFAULT_UID);
|
||||
setGid(DEFAULT_GID);
|
||||
setMtime(DateTime.now(UTC));
|
||||
setType(DEFAULT_TYPE);
|
||||
setMagic();
|
||||
setVersion();
|
||||
@@ -418,6 +418,7 @@ public final class PosixTarHeader {
|
||||
public Builder setMtime(DateTime mtime) {
|
||||
checkNotNull(mtime, "mtime");
|
||||
setField("mtime", 136, 12, String.format("%011o", mtime.getMillis() / MILLIS_PER_SECOND));
|
||||
hasMtime = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -483,8 +484,10 @@ public final class PosixTarHeader {
|
||||
public PosixTarHeader build() {
|
||||
checkState(hasName, "name not set");
|
||||
checkState(hasSize, "size not set");
|
||||
checkState(hasMtime, "mtime not set");
|
||||
hasName = false;
|
||||
hasSize = false;
|
||||
hasMtime = false;
|
||||
setChksum(); // Calculate the checksum last.
|
||||
return new PosixTarHeader(header.clone());
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.math.BigInteger;
|
||||
@@ -42,8 +41,6 @@ import org.joda.time.DateTime;
|
||||
public class SelfSignedCaCertificate {
|
||||
|
||||
private static final String DEFAULT_ISSUER_FQDN = "registry-test";
|
||||
private static final DateTime DEFAULT_NOT_BEFORE = DateTime.now(UTC).minusHours(1);
|
||||
private static final DateTime DEFAULT_NOT_AFTER = DateTime.now(UTC).plusDays(1);
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
|
||||
@@ -68,13 +65,16 @@ public class SelfSignedCaCertificate {
|
||||
return cert;
|
||||
}
|
||||
|
||||
public static SelfSignedCaCertificate create() throws Exception {
|
||||
public static SelfSignedCaCertificate create(Clock clock) throws Exception {
|
||||
return create(
|
||||
keyGen.generateKeyPair(), DEFAULT_ISSUER_FQDN, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
|
||||
keyGen.generateKeyPair(),
|
||||
DEFAULT_ISSUER_FQDN,
|
||||
clock.nowUtc().minusHours(1),
|
||||
clock.nowUtc().plusDays(1));
|
||||
}
|
||||
|
||||
public static SelfSignedCaCertificate create(String fqdn) throws Exception {
|
||||
return create(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
|
||||
public static SelfSignedCaCertificate create(String fqdn, Clock clock) throws Exception {
|
||||
return create(fqdn, clock.nowUtc().minusHours(1), clock.nowUtc().plusDays(1));
|
||||
}
|
||||
|
||||
public static SelfSignedCaCertificate create(String fqdn, DateTime from, DateTime to)
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -199,7 +200,11 @@ class PosixTarHeaderTest {
|
||||
@Test
|
||||
void testBadChecksum() {
|
||||
PosixTarHeader header =
|
||||
new PosixTarHeader.Builder().setName("(◕‿◕).txt").setSize(31337).build();
|
||||
new PosixTarHeader.Builder()
|
||||
.setName("(◕‿◕).txt")
|
||||
.setSize(31337)
|
||||
.setMtime(DateTime.now(DateTimeZone.UTC))
|
||||
.build();
|
||||
byte[] bytes = header.getBytes();
|
||||
bytes[150] = '0';
|
||||
bytes[151] = '0';
|
||||
@@ -234,6 +239,7 @@ class PosixTarHeaderTest {
|
||||
new PosixTarHeader.Builder()
|
||||
.setName("(•︵•).txt") // Awwwww! It looks so sad...
|
||||
.setSize(123)
|
||||
.setMtime(DateTime.now(DateTimeZone.UTC))
|
||||
.build())
|
||||
.testEquals();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user