mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d0af81ecdf | |||
| f273783894 | |||
| 0dfabe1c64 | |||
| 60a011c593 | |||
| 7716eebfff | |||
| 1e76eeed37 | |||
| 147d133aef | |||
| c2e1f2e640 | |||
| 5d2639834a | |||
| 7912576e3d | |||
| 8424c85258 | |||
| e72dd73ed8 | |||
| 508d221b94 | |||
| 073d0a416a | |||
| f2ead5a0e3 | |||
| 212dbbe520 | |||
| 8594a61fd4 | |||
| 36837eb3e6 | |||
| 3a9a8c6557 | |||
| 65c2570b8f | |||
| 86acaa1b31 | |||
| 436cc03be9 | |||
| e110ddd412 | |||
| 214b23e99c | |||
| 743dea9ca2 | |||
| 41f9f1ef7d | |||
| 44ede2b022 | |||
| e4312322dc |
@@ -36,3 +36,4 @@ Shicong Huang <shicong@google.com>
|
||||
Gustav Brodman <gbrodman@google.com>
|
||||
Sarah Botwinick <sarahbot@google.com>
|
||||
Legina Chen <legina@google.com>
|
||||
Rachel Guan <rachelguan@google.com>
|
||||
|
||||
@@ -331,6 +331,13 @@ subprojects {
|
||||
|
||||
apply from: "${rootDir.path}/java_common.gradle"
|
||||
|
||||
if (project.name != 'docs') {
|
||||
compileJava {
|
||||
// TODO: Remove this once we migrate off AppEngine.
|
||||
options.release = 8
|
||||
}
|
||||
}
|
||||
|
||||
if (project.name == 'third_party') return
|
||||
|
||||
project.tasks.test.dependsOn runPresubmits
|
||||
|
||||
@@ -51,6 +51,7 @@ artifacts {
|
||||
dependencies {
|
||||
def deps = rootProject.dependencyMap
|
||||
|
||||
compile deps['com.github.ben-manes.caffeine:caffeine']
|
||||
compile deps['com.google.code.findbugs:jsr305']
|
||||
compile deps['com.google.guava:guava']
|
||||
compile deps['javax.inject:javax.inject']
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
|
||||
com.google.j2objc:j2objc-annotations:1.3
|
||||
javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
org.checkerframework:checker-qual:3.8.0
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -16,7 +17,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -16,7 +17,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -17,7 +18,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -17,7 +18,7 @@ joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.apiguardian:apiguardian-api:1.1.0
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.junit.jupiter:junit-jupiter-api:5.6.2
|
||||
org.junit.jupiter:junit-jupiter-engine:5.6.2
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -15,6 +16,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
com.google.guava:guava:30.1.1-jre
|
||||
@@ -15,6 +16,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -16,6 +17,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.google.auto.value:auto-value-annotations:1.7.4
|
||||
com.google.code.findbugs:jsr305:3.0.2
|
||||
com.google.errorprone:error_prone_annotations:2.5.1
|
||||
com.google.errorprone:error_prone_annotations:2.10.0
|
||||
com.google.flogger:flogger-system-backend:0.7.4
|
||||
com.google.flogger:flogger:0.7.4
|
||||
com.google.guava:failureaccess:1.0.1
|
||||
@@ -16,6 +17,6 @@ javax.inject:javax.inject:1
|
||||
joda-time:joda-time:2.9.2
|
||||
junit:junit:4.13.1
|
||||
org.checkerframework:checker-compat-qual:2.5.3
|
||||
org.checkerframework:checker-qual:3.9.1
|
||||
org.checkerframework:checker-qual:3.19.0
|
||||
org.hamcrest:hamcrest-core:1.3
|
||||
org.ow2.asm:asm:9.0
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
package google.registry.testing;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -29,7 +29,7 @@ public final class SystemInfo {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final LoadingCache<String, Boolean> hasCommandCache =
|
||||
CacheBuilder.newBuilder()
|
||||
Caffeine.newBuilder()
|
||||
.build(
|
||||
new CacheLoader<String, Boolean>() {
|
||||
@Override
|
||||
|
||||
@@ -172,6 +172,7 @@ dependencies {
|
||||
testRuntime files(sourceSets.test.resources.srcDirs)
|
||||
|
||||
compile deps['com.beust:jcommander']
|
||||
compile deps['com.github.ben-manes.caffeine:caffeine']
|
||||
compile deps['com.google.api:gax']
|
||||
compile deps['com.google.api.grpc:proto-google-cloud-datastore-v1']
|
||||
compile deps['com.google.api.grpc:proto-google-common-protos']
|
||||
@@ -799,6 +800,11 @@ if (environment == 'alpha') {
|
||||
mainClass: 'google.registry.beam.comparedb.ValidateDatabasePipeline',
|
||||
metaData: 'google/registry/beam/validate_database_pipeline_metadata.json'
|
||||
],
|
||||
resaveAllEppResources:
|
||||
[
|
||||
mainClass: 'google.registry.beam.resave.ResaveAllEppResourcesPipeline',
|
||||
metaData: 'google/registry/beam/resave_all_epp_resources_pipeline_metadata.json'
|
||||
],
|
||||
]
|
||||
project.tasks.create("stageBeamPipelines") {
|
||||
doLast {
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -13,6 +13,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -17,6 +17,7 @@ com.fasterxml.jackson.core:jackson-databind:2.13.0
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0
|
||||
com.fasterxml.jackson:jackson-bom:2.13.0
|
||||
com.fasterxml:classmate:1.5.1
|
||||
com.github.ben-manes.caffeine:caffeine:2.9.3
|
||||
com.github.docker-java:docker-java-api:3.2.7
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.2.7
|
||||
com.github.docker-java:docker-java-transport:3.2.7
|
||||
|
||||
@@ -112,6 +112,12 @@ public class BatchModule {
|
||||
req, ExpandRecurringBillingEventsAction.PARAM_CURSOR_TIME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(ResaveAllEppResourcesPipelineAction.PARAM_FAST)
|
||||
static Optional<Boolean> provideIsFast(HttpServletRequest req) {
|
||||
return extractOptionalBooleanParameter(req, ResaveAllEppResourcesPipelineAction.PARAM_FAST);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(QUEUE_ASYNC_ACTIONS)
|
||||
static Queue provideAsyncActionsPushQueue() {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static google.registry.beam.BeamUtils.createJobName;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateParameter;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateRequest;
|
||||
import com.google.api.services.dataflow.model.LaunchFlexTemplateResponse;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Starts a Dataflow pipeline that resaves all EPP resources projected to the current time.
|
||||
*
|
||||
* <p>This is useful for a few situations. First, as a fallback option for resource transfers that
|
||||
* have expired pending transfers (this will resolve them), just in case the enqueued action fails.
|
||||
* Second, it will reflect domain autorenews that have happened. Third, it will remove any expired
|
||||
* grace periods.
|
||||
*
|
||||
* <p>There's also the general principle that it's good to have the data in the database remain as
|
||||
* current as is reasonably possible.
|
||||
*
|
||||
* <p>If the <code>?isFast=true</code> query string parameter is passed as true, the pipeline will
|
||||
* only attempt to load, project, and resave entities where we expect one of the previous situations
|
||||
* has occurred. Otherwise, we will load, project, and resave all EPP resources.
|
||||
*
|
||||
* <p>This runs the {@link google.registry.beam.resave.ResaveAllEppResourcesPipeline}.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = ResaveAllEppResourcesPipelineAction.PATH,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public class ResaveAllEppResourcesPipelineAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
static final String PATH = "/_dr/task/resaveAllEppResourcesPipeline";
|
||||
static final String PIPELINE_NAME = "resave_all_epp_resources_pipeline";
|
||||
|
||||
public static final String PARAM_FAST = "fast";
|
||||
|
||||
private final String projectId;
|
||||
private final String jobRegion;
|
||||
private final String stagingBucketUrl;
|
||||
private final boolean fast;
|
||||
private final Clock clock;
|
||||
private final Response response;
|
||||
private final Dataflow dataflow;
|
||||
|
||||
@Inject
|
||||
ResaveAllEppResourcesPipelineAction(
|
||||
@Config("projectId") String projectId,
|
||||
@Config("defaultJobRegion") String jobRegion,
|
||||
@Config("beamStagingBucketUrl") String stagingBucketUrl,
|
||||
@Parameter(PARAM_FAST) Optional<Boolean> fast,
|
||||
Clock clock,
|
||||
Response response,
|
||||
Dataflow dataflow) {
|
||||
this.projectId = projectId;
|
||||
this.jobRegion = jobRegion;
|
||||
this.stagingBucketUrl = stagingBucketUrl;
|
||||
this.fast = fast.orElse(false);
|
||||
this.clock = clock;
|
||||
this.response = response;
|
||||
this.dataflow = dataflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
logger.atInfo().log("Launching ResaveAllEppResourcesPipeline");
|
||||
try {
|
||||
LaunchFlexTemplateParameter parameter =
|
||||
new LaunchFlexTemplateParameter()
|
||||
.setJobName(createJobName("resave-all-epp-resources", clock))
|
||||
.setContainerSpecGcsPath(
|
||||
String.format("%s/%s_metadata.json", stagingBucketUrl, PIPELINE_NAME))
|
||||
.setParameters(
|
||||
new ImmutableMap.Builder<String, String>()
|
||||
.put(PARAM_FAST, Boolean.toString(fast))
|
||||
.put("registryEnvironment", RegistryEnvironment.get().name())
|
||||
.build());
|
||||
LaunchFlexTemplateResponse launchResponse =
|
||||
dataflow
|
||||
.projects()
|
||||
.locations()
|
||||
.flexTemplates()
|
||||
.launch(
|
||||
projectId,
|
||||
jobRegion,
|
||||
new LaunchFlexTemplateRequest().setLaunchParameter(parameter))
|
||||
.execute();
|
||||
logger.atInfo().log("Got response: %s", launchResponse.getJob().toPrettyString());
|
||||
String jobId = launchResponse.getJob().getId();
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload(String.format("Launched resaveAllEppResources pipeline: %s", jobId));
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Template Launch failed.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Pipeline launch failed: %s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ public class InitSqlPipeline implements Serializable {
|
||||
.addAll(toKindStrings(PHASE_TWO_ORDERED))
|
||||
.build()));
|
||||
|
||||
// Set up the pipeline to write entity kinds from PHASE_ONE_ORDERED to SQL. Return a object
|
||||
// Set up the pipeline to write entity kinds from PHASE_ONE_ORDERED to SQL. Return an object
|
||||
// that signals the completion of the phase.
|
||||
PCollection<Void> blocker =
|
||||
scheduleOnePhaseWrites(datastoreSnapshot, PHASE_ONE_ORDERED, Optional.empty(), null);
|
||||
|
||||
@@ -22,7 +22,7 @@ import static google.registry.beam.initsql.BackupPaths.getExportFilePatterns;
|
||||
import static google.registry.model.ofy.ObjectifyService.auditedOfy;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.kvs;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
|
||||
@@ -343,11 +343,11 @@ public final class Transforms {
|
||||
// Canonicalize old domain/host names from 2016 and earlier before we were enforcing this.
|
||||
entity.setIndexedProperty(
|
||||
"fullyQualifiedDomainName",
|
||||
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedDomainName")));
|
||||
canonicalizeHostname((String) entity.getProperty("fullyQualifiedDomainName")));
|
||||
} else if (entity.getKind().equals("HostResource")) {
|
||||
entity.setIndexedProperty(
|
||||
"fullyQualifiedHostName",
|
||||
canonicalizeDomainName((String) entity.getProperty("fullyQualifiedHostName")));
|
||||
canonicalizeHostname((String) entity.getProperty("fullyQualifiedHostName")));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.beam.BeamUtils.getQueryFromFile;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.strings;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryJpaIO.Read;
|
||||
import google.registry.beam.invoicing.BillingEvent.InvoiceGroupingKey;
|
||||
@@ -35,6 +36,7 @@ import java.time.LocalTime;
|
||||
import java.time.YearMonth;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
@@ -53,6 +55,7 @@ import org.apache.beam.sdk.transforms.PTransform;
|
||||
import org.apache.beam.sdk.values.KV;
|
||||
import org.apache.beam.sdk.values.PCollection;
|
||||
import org.apache.beam.sdk.values.TypeDescriptor;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
/**
|
||||
* Definition of a Dataflow Flex pipeline template, which generates a given month's invoices.
|
||||
@@ -73,6 +76,8 @@ public class InvoicingPipeline implements Serializable {
|
||||
private static final Pattern SQL_COMMENT_REGEX =
|
||||
Pattern.compile("^\\s*--.*\\n", Pattern.MULTILINE);
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final InvoicingPipelineOptions options;
|
||||
|
||||
InvoicingPipeline(InvoicingPipelineOptions options) {
|
||||
@@ -113,31 +118,44 @@ public class InvoicingPipeline implements Serializable {
|
||||
InvoicingPipelineOptions options, Pipeline pipeline) {
|
||||
Read<Object[], BillingEvent> read =
|
||||
RegistryJpaIO.read(
|
||||
makeCloudSqlQuery(options.getYearMonth()), false, InvoicingPipeline::parseRow);
|
||||
makeCloudSqlQuery(options.getYearMonth()), false, row -> parseRow(row).orElse(null));
|
||||
|
||||
return pipeline.apply("Read BillingEvents from Cloud SQL", read);
|
||||
PCollection<BillingEvent> billingEventsWithNulls =
|
||||
pipeline.apply("Read BillingEvents from Cloud SQL", read);
|
||||
|
||||
// Remove null billing events
|
||||
return billingEventsWithNulls.apply(Filter.by(Objects::nonNull));
|
||||
}
|
||||
|
||||
private static BillingEvent parseRow(Object[] row) {
|
||||
private static Optional<BillingEvent> parseRow(Object[] row) {
|
||||
google.registry.model.billing.BillingEvent.OneTime oneTime =
|
||||
(google.registry.model.billing.BillingEvent.OneTime) row[0];
|
||||
Registrar registrar = (Registrar) row[1];
|
||||
return BillingEvent.create(
|
||||
oneTime.getId(),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
|
||||
registrar.getRegistrarId(),
|
||||
registrar.getBillingIdentifier().toString(),
|
||||
registrar.getPoNumber().orElse(""),
|
||||
DomainNameUtils.getTldFromDomainName(oneTime.getTargetId()),
|
||||
oneTime.getReason().toString(),
|
||||
oneTime.getTargetId(),
|
||||
oneTime.getDomainRepoId(),
|
||||
Optional.ofNullable(oneTime.getPeriodYears()).orElse(0),
|
||||
oneTime.getCost().getCurrencyUnit().toString(),
|
||||
oneTime.getCost().getAmount().doubleValue(),
|
||||
String.join(
|
||||
" ", oneTime.getFlags().stream().map(Flag::toString).collect(toImmutableSet())));
|
||||
CurrencyUnit currency = oneTime.getCost().getCurrencyUnit();
|
||||
if (!registrar.getBillingAccountMap().containsKey(currency)) {
|
||||
logger.atSevere().log(
|
||||
"Registrar %s does not have a product account key for the currency unit: %s",
|
||||
registrar.getRegistrarId(), currency);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(
|
||||
BillingEvent.create(
|
||||
oneTime.getId(),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getBillingTime(), ZoneId.of("UTC")),
|
||||
DateTimeUtils.toZonedDateTime(oneTime.getEventTime(), ZoneId.of("UTC")),
|
||||
registrar.getRegistrarId(),
|
||||
registrar.getBillingAccountMap().get(currency),
|
||||
registrar.getPoNumber().orElse(""),
|
||||
DomainNameUtils.getTldFromDomainName(oneTime.getTargetId()),
|
||||
oneTime.getReason().toString(),
|
||||
oneTime.getTargetId(),
|
||||
oneTime.getDomainRepoId(),
|
||||
Optional.ofNullable(oneTime.getPeriodYears()).orElse(0),
|
||||
oneTime.getCost().getCurrencyUnit().toString(),
|
||||
oneTime.getCost().getAmount().doubleValue(),
|
||||
String.join(
|
||||
" ", oneTime.getFlags().stream().map(Flag::toString).collect(toImmutableSet()))));
|
||||
}
|
||||
|
||||
/** Transform that converts a {@code BillingEvent} into an invoice CSV row. */
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.beam.rde;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.beam.rde.RdePipeline.TupleTags.DOMAIN_FRAGMENTS;
|
||||
import static google.registry.beam.rde.RdePipeline.TupleTags.EXTERNAL_HOST_FRAGMENTS;
|
||||
@@ -28,10 +29,12 @@ import static google.registry.model.reporting.HistoryEntryDao.RESOURCE_TYPES_TO_
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.kvs;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Component;
|
||||
@@ -198,6 +201,8 @@ public class RdePipeline implements Serializable {
|
||||
HostHistory.class,
|
||||
"hostBase");
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject
|
||||
RdePipeline(RdePipelineOptions options, GcsUtils gcsUtils, CloudTasksUtils cloudTasksUtils) {
|
||||
this.options = options;
|
||||
@@ -357,6 +362,33 @@ public class RdePipeline implements Serializable {
|
||||
.setCoder(KvCoder.of(StringUtf8Coder.of(), VarLongCoder.of()));
|
||||
}
|
||||
|
||||
private <T extends HistoryEntry> EppResource loadResourceByHistoryEntryId(
|
||||
Class<T> historyEntryClazz, String repoId, Iterable<Long> revisionIds) {
|
||||
ImmutableList<Long> ids = ImmutableList.copyOf(revisionIds);
|
||||
// The size should always be 1 because we are only getting one repo ID -> revision ID pair per
|
||||
// repo ID from the source transform (the JPA query in the method above). But for some reason
|
||||
// after CoGroupByKey (joining the revision IDs and the pending deposits on repo IDs), in
|
||||
// #removedUnreferencedResources, duplicate revision IDs are sometimes introduced. Here we
|
||||
// attempt to deduplicate the iterable. If it contains multiple revision IDs that are NOT the
|
||||
// same, we have a more serious problem as we cannot be sure which one to use. We should use the
|
||||
// highest revision ID, but we don't even know where it comes from, as the query should
|
||||
// definitively only give us one revision ID per repo ID. In this case we have to abort and
|
||||
// require manual intervention.
|
||||
if (ids.size() != 1) {
|
||||
ImmutableSet<Long> dedupedIds = ImmutableSet.copyOf(ids);
|
||||
checkState(
|
||||
dedupedIds.size() == 1,
|
||||
"Multiple unique revision IDs detected for %s repo ID %s: %s",
|
||||
EPP_RESOURCE_FIELD_NAME.get(historyEntryClazz),
|
||||
repoId,
|
||||
ids);
|
||||
logger.atSevere().log(
|
||||
"Duplicate revision IDs detected for %s repo ID %s: %s",
|
||||
EPP_RESOURCE_FIELD_NAME.get(historyEntryClazz), repoId, ids);
|
||||
}
|
||||
return loadResourceByHistoryEntryId(historyEntryClazz, repoId, ids.get(0));
|
||||
}
|
||||
|
||||
private <T extends HistoryEntry> EppResource loadResourceByHistoryEntryId(
|
||||
Class<T> historyEntryClazz, String repoId, long revisionId) {
|
||||
try {
|
||||
@@ -516,7 +548,7 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
ContactHistory.class,
|
||||
kv.getKey(),
|
||||
kv.getValue().getOnly(REVISION_ID));
|
||||
kv.getValue().getAll(REVISION_ID));
|
||||
DepositFragment fragment = marshaller.marshalContact(contact);
|
||||
ImmutableSet<KV<PendingDeposit, DepositFragment>> fragments =
|
||||
Streams.stream(kv.getValue().getAll(PENDING_DEPOSIT))
|
||||
@@ -549,8 +581,8 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
HostHistory.class,
|
||||
kv.getKey(),
|
||||
kv.getValue().getOnly(REVISION_ID));
|
||||
// When a host is subordinate, we need to find it's superordinate domain and
|
||||
kv.getValue().getAll(REVISION_ID));
|
||||
// When a host is subordinate, we need to find its superordinate domain and
|
||||
// include it in the deposit as well.
|
||||
if (host.isSubordinate()) {
|
||||
subordinateHostCounter.inc();
|
||||
@@ -627,7 +659,7 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
DomainHistory.class,
|
||||
kv.getKey(),
|
||||
kv.getValue().getOnly(REVISION_ID));
|
||||
kv.getValue().getAll(REVISION_ID));
|
||||
ImmutableSet.Builder<KV<PendingDeposit, DepositFragment>> results =
|
||||
new ImmutableSet.Builder<>();
|
||||
for (KV<String, CoGbkResult> hostToPendingDeposits :
|
||||
@@ -637,7 +669,7 @@ public class RdePipeline implements Serializable {
|
||||
loadResourceByHistoryEntryId(
|
||||
HostHistory.class,
|
||||
hostToPendingDeposits.getKey(),
|
||||
hostToPendingDeposits.getValue().getOnly(REVISION_ID));
|
||||
hostToPendingDeposits.getValue().getAll(REVISION_ID));
|
||||
DepositFragment fragment =
|
||||
marshaller.marshalSubordinateHost(host, superordinateDomain);
|
||||
Streams.stream(hostToPendingDeposits.getValue().getAll(PENDING_DEPOSIT))
|
||||
@@ -696,6 +728,7 @@ public class RdePipeline implements Serializable {
|
||||
* CoGbkResult}s are used.
|
||||
*/
|
||||
protected abstract static class TupleTags {
|
||||
|
||||
protected static final TupleTag<KV<PendingDeposit, DepositFragment>> DOMAIN_FRAGMENTS =
|
||||
new TupleTag<KV<PendingDeposit, DepositFragment>>() {};
|
||||
|
||||
@@ -729,10 +762,12 @@ public class RdePipeline implements Serializable {
|
||||
UtilsModule.class
|
||||
})
|
||||
interface RdePipelineComponent {
|
||||
|
||||
RdePipeline rdePipeline();
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder options(RdePipelineOptions options);
|
||||
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.beam.resave;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static org.apache.beam.sdk.values.TypeDescriptors.integers;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.beam.common.RegistryJpaIO;
|
||||
import google.registry.beam.common.RegistryJpaIO.Read;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.ContactResource;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.util.DateTimeUtils;
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.apache.beam.sdk.Pipeline;
|
||||
import org.apache.beam.sdk.PipelineResult;
|
||||
import org.apache.beam.sdk.transforms.DoFn;
|
||||
import org.apache.beam.sdk.transforms.GroupIntoBatches;
|
||||
import org.apache.beam.sdk.transforms.ParDo;
|
||||
import org.apache.beam.sdk.transforms.WithKeys;
|
||||
import org.apache.beam.sdk.util.ShardedKey;
|
||||
import org.apache.beam.sdk.values.KV;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A Dataflow Flex pipeline that resaves changed EPP resources in SQL.
|
||||
*
|
||||
* <p>Due to the way that Hibernate works, if an entity is unchanged by {@link
|
||||
* EppResource#cloneProjectedAtTime(DateTime)} it will not actually be re-persisted to the database.
|
||||
* Thus, the only actual changes occur when objects are changed by projecting them to now, such as
|
||||
* when a pending transfer is resolved.
|
||||
*/
|
||||
public class ResaveAllEppResourcesPipeline implements Serializable {
|
||||
|
||||
private static final ImmutableSet<Class<? extends EppResource>> EPP_RESOURCE_CLASSES =
|
||||
ImmutableSet.of(ContactResource.class, DomainBase.class, HostResource.class);
|
||||
|
||||
/**
|
||||
* There exist three possible situations where we know we'll want to project domains to the
|
||||
* current point in time:
|
||||
*
|
||||
* <ul>
|
||||
* <li>A pending domain transfer has expired.
|
||||
* <li>A domain is past its expiration time without being deleted (this means it autorenewed).
|
||||
* <li>A domain has expired grace periods.
|
||||
* </ul>
|
||||
*
|
||||
* <p>This command contains all three scenarios so that we can avoid querying the Domain table
|
||||
* multiple times, and to avoid projecting and resaving the same domain multiple times.
|
||||
*/
|
||||
private static final String DOMAINS_TO_PROJECT_QUERY =
|
||||
"FROM Domain d WHERE (d.transferData.transferStatus = 'PENDING' AND"
|
||||
+ " d.transferData.pendingTransferExpirationTime < current_timestamp()) OR"
|
||||
+ " (d.registrationExpirationTime < current_timestamp() AND d.deletionTime ="
|
||||
+ " (:END_OF_TIME)) OR (EXISTS (SELECT 1 FROM GracePeriod gp WHERE gp.domainRepoId ="
|
||||
+ " d.repoId AND gp.expirationTime < current_timestamp()))";
|
||||
|
||||
private final ResaveAllEppResourcesPipelineOptions options;
|
||||
|
||||
ResaveAllEppResourcesPipeline(ResaveAllEppResourcesPipelineOptions options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
PipelineResult run() {
|
||||
Pipeline pipeline = Pipeline.create(options);
|
||||
setupPipeline(pipeline);
|
||||
return pipeline.run();
|
||||
}
|
||||
|
||||
void setupPipeline(Pipeline pipeline) {
|
||||
options.setIsolationOverride(TransactionIsolationLevel.TRANSACTION_READ_COMMITTED);
|
||||
if (options.getFast()) {
|
||||
fastResaveContacts(pipeline);
|
||||
fastResaveDomains(pipeline);
|
||||
} else {
|
||||
EPP_RESOURCE_CLASSES.forEach(clazz -> forceResaveAllResources(pipeline, clazz));
|
||||
}
|
||||
}
|
||||
|
||||
/** Projects to the current time and saves any contacts with expired transfers. */
|
||||
private void fastResaveContacts(Pipeline pipeline) {
|
||||
Read<ContactResource, ContactResource> read =
|
||||
RegistryJpaIO.read(
|
||||
"FROM Contact WHERE transferData.transferStatus = 'PENDING' AND"
|
||||
+ " transferData.pendingTransferExpirationTime < current_timestamp()",
|
||||
ContactResource.class,
|
||||
c -> c);
|
||||
projectAndResaveResources(pipeline, ContactResource.class, read);
|
||||
}
|
||||
|
||||
/**
|
||||
* Projects to the current time and saves any domains with expired pending actions (e.g.
|
||||
* transfers, grace periods).
|
||||
*
|
||||
* <p>The logic of what might have changed is paraphrased from {@link
|
||||
* google.registry.model.domain.DomainContent#cloneProjectedAtTime(DateTime)}.
|
||||
*/
|
||||
private void fastResaveDomains(Pipeline pipeline) {
|
||||
Read<DomainBase, DomainBase> read =
|
||||
RegistryJpaIO.read(
|
||||
DOMAINS_TO_PROJECT_QUERY,
|
||||
ImmutableMap.of("END_OF_TIME", DateTimeUtils.END_OF_TIME),
|
||||
DomainBase.class,
|
||||
d -> d);
|
||||
projectAndResaveResources(pipeline, DomainBase.class, read);
|
||||
}
|
||||
|
||||
/** Projects all resources to the current time and saves them. */
|
||||
private <T extends EppResource> void forceResaveAllResources(Pipeline pipeline, Class<T> clazz) {
|
||||
Read<T, T> read = RegistryJpaIO.read(() -> CriteriaQueryBuilder.create(clazz).build());
|
||||
projectAndResaveResources(pipeline, clazz, read);
|
||||
}
|
||||
|
||||
/** Projects and re-saves the result of the provided {@link Read}. */
|
||||
private <T extends EppResource> void projectAndResaveResources(
|
||||
Pipeline pipeline, Class<T> clazz, Read<?, T> read) {
|
||||
int numShards = options.getSqlWriteShards();
|
||||
int batchSize = options.getSqlWriteBatchSize();
|
||||
String className = clazz.getSimpleName();
|
||||
pipeline
|
||||
.apply("Read " + className, read)
|
||||
.apply(
|
||||
"Shard data for class" + className,
|
||||
WithKeys.<Integer, T>of(e -> ThreadLocalRandom.current().nextInt(numShards))
|
||||
.withKeyType(integers()))
|
||||
.apply(
|
||||
"Group into batches for class" + className,
|
||||
GroupIntoBatches.<Integer, T>ofSize(batchSize).withShardedKey())
|
||||
.apply("Map " + className + " to now", ParDo.of(new BatchedProjectionFunction<>()))
|
||||
.apply(
|
||||
"Write transformed " + className,
|
||||
RegistryJpaIO.<EppResource>write()
|
||||
.withName("Write transformed " + className)
|
||||
.withBatchSize(batchSize)
|
||||
.withShards(numShards));
|
||||
}
|
||||
|
||||
private static class BatchedProjectionFunction<T extends EppResource>
|
||||
extends DoFn<KV<ShardedKey<Integer>, Iterable<T>>, EppResource> {
|
||||
|
||||
@ProcessElement
|
||||
public void processElement(
|
||||
@Element KV<ShardedKey<Integer>, Iterable<T>> element,
|
||||
OutputReceiver<EppResource> outputReceiver) {
|
||||
jpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
element
|
||||
.getValue()
|
||||
.forEach(
|
||||
resource ->
|
||||
outputReceiver.output(
|
||||
resource.cloneProjectedAtTime(jpaTm().getTransactionTime()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.beam.resave;
|
||||
|
||||
import google.registry.beam.common.RegistryPipelineOptions;
|
||||
import org.apache.beam.sdk.options.Description;
|
||||
|
||||
public interface ResaveAllEppResourcesPipelineOptions extends RegistryPipelineOptions {
|
||||
|
||||
@Description("True if we should attempt to run only over potentially out-of-date EPP resources")
|
||||
boolean getFast();
|
||||
|
||||
void setFast(boolean fast);
|
||||
}
|
||||
@@ -1438,8 +1438,8 @@ public final class RegistryConfig {
|
||||
}
|
||||
|
||||
/** Returns the amount of time a singleton should be cached, before expiring. */
|
||||
public static Duration getSingletonCacheRefreshDuration() {
|
||||
return Duration.standardSeconds(CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds);
|
||||
public static java.time.Duration getSingletonCacheRefreshDuration() {
|
||||
return java.time.Duration.ofSeconds(CONFIG_SETTINGS.get().caching.singletonCacheRefreshSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -316,6 +316,12 @@
|
||||
<url-pattern>/_dr/task/resaveAllEppResources</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Dataflow pipeline to re-save all EPP resources. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
<url-pattern>/_dr/task/resaveAllEppResourcesPipeline</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Reread all Registrar RDAP Base Urls from the ICANN endpoint. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<blacklistentries>
|
||||
|
||||
<!-- Example IPv4 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>1.2.3.4/24</subnet>
|
||||
<description>An IPv4 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
<!-- Example IPv6 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>abcd::123:4567/48</subnet>
|
||||
<description>An IPv6 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
</blacklistentries>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<blacklistentries>
|
||||
|
||||
<!-- Example IPv4 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>1.2.3.4/24</subnet>
|
||||
<description>An IPv4 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
<!-- Example IPv6 CIDR Subnet
|
||||
<blacklist>
|
||||
<subnet>abcd::123:4567/48</subnet>
|
||||
<description>An IPv6 subnet</description>
|
||||
</blacklist> -->
|
||||
|
||||
</blacklistentries>
|
||||
@@ -36,19 +36,6 @@
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url>/_dr/task/rdeStaging?beam=true</url>
|
||||
<description>
|
||||
This job generates a full RDE escrow deposit as a single gigantic XML
|
||||
document using the Beam pipeline regardless of the current TM
|
||||
configuration and streams it to cloud storage. It does not trigger the
|
||||
subsequent upload tasks and is meant to run parallel with the main cron
|
||||
job in order to compare the results from both runs.
|
||||
</description>
|
||||
<schedule>every 8 hours from 00:07 to 20:00</schedule>
|
||||
<target>backend</target>
|
||||
</cron>
|
||||
|
||||
<cron>
|
||||
<url><![CDATA[/_dr/cron/fanout?queue=rde-upload&endpoint=/_dr/task/rdeUpload&forEachRealTld]]></url>
|
||||
<description>
|
||||
|
||||
@@ -31,7 +31,7 @@ import static google.registry.monitoring.whitebox.CheckApiMetric.Status.UNKNOWN_
|
||||
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM;
|
||||
import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static org.json.simple.JSONValue.toJSONString;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -105,7 +105,7 @@ public class CheckApiAction implements Runnable {
|
||||
String domainString;
|
||||
InternetDomainName domainName;
|
||||
try {
|
||||
domainString = canonicalizeDomainName(nullToEmpty(domain));
|
||||
domainString = canonicalizeHostname(nullToEmpty(domain));
|
||||
domainName = validateDomainName(domainString);
|
||||
} catch (IllegalArgumentException | EppException e) {
|
||||
metricBuilder.status(INVALID_NAME);
|
||||
|
||||
@@ -25,6 +25,7 @@ import static google.registry.model.ResourceTransferUtils.handlePendingTransferO
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.model.transfer.TransferStatus.SERVER_CANCELLED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -94,6 +95,7 @@ public final class ContactDeleteFlow implements TransactionalFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
assertAsyncActionsAreAllowed();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
checkLinkedDomains(targetId, now, ContactResource.class, DomainBase::getReferencedContacts);
|
||||
ContactResource existingContact = loadAndVerifyExistence(ContactResource.class, targetId, now);
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -96,6 +97,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
assertAsyncActionsAreAllowed();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
validateHostName(targetId);
|
||||
checkLinkedDomains(targetId, now, HostResource.class, DomainBase::getNameservers);
|
||||
|
||||
@@ -28,6 +28,7 @@ import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomain
|
||||
import static google.registry.flows.host.HostFlowUtils.verifySuperordinateDomainOwnership;
|
||||
import static google.registry.model.index.ForeignKeyIndex.loadAndGetKey;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.HOST_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.assertAsyncActionsAreAllowed;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
@@ -136,6 +137,9 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
validateHostName(targetId);
|
||||
HostResource existingHost = loadAndVerifyExistence(HostResource.class, targetId, now);
|
||||
boolean isHostRename = suppliedNewHostName != null;
|
||||
if (isHostRename) {
|
||||
assertAsyncActionsAreAllowed();
|
||||
}
|
||||
String oldHostName = targetId;
|
||||
String newHostName = firstNonNull(suppliedNewHostName, oldHostName);
|
||||
DomainBase oldSuperordinateDomain =
|
||||
|
||||
@@ -17,10 +17,10 @@ package google.registry.model;
|
||||
import static com.google.common.base.Suppliers.memoizeWithExpiration;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.joda.time.Duration.ZERO;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.common.base.Supplier;
|
||||
import org.joda.time.Duration;
|
||||
import java.time.Duration;
|
||||
|
||||
/** Utility methods related to caching Datastore entities. */
|
||||
public class CacheUtils {
|
||||
@@ -41,8 +41,36 @@ public class CacheUtils {
|
||||
*/
|
||||
public static <T> Supplier<T> tryMemoizeWithExpiration(
|
||||
Duration expiration, Supplier<T> original) {
|
||||
return expiration.isEqual(ZERO)
|
||||
return expiration.isZero()
|
||||
? original
|
||||
: memoizeWithExpiration(original, expiration.getMillis(), MILLISECONDS);
|
||||
: memoizeWithExpiration(original, expiration.toMillis(), MILLISECONDS);
|
||||
}
|
||||
|
||||
/** Creates and returns a new {@link Caffeine} builder. */
|
||||
public static Caffeine<Object, Object> newCacheBuilder() {
|
||||
return Caffeine.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link Caffeine} builder with the specified cache expiration.
|
||||
*
|
||||
* <p>This also sets the refresh duration to half of the cache expiration. The resultant behavior
|
||||
* is that a cache entry is eligible to be asynchronously refreshed after access once more than
|
||||
* half of its cache duration has elapsed, and then it is synchronously refreshed (blocking the
|
||||
* read) once its full cache duration has elapsed. So you will never get data older than the cache
|
||||
* expiration, but for frequently accessed keys it will be refreshed more often than that and the
|
||||
* cost of the load will never be incurred during the read.
|
||||
*/
|
||||
public static Caffeine<Object, Object> newCacheBuilder(Duration expireAfterWrite) {
|
||||
Duration refreshAfterWrite = expireAfterWrite.dividedBy(2);
|
||||
Caffeine<Object, Object> caffeine = Caffeine.newBuilder().expireAfterWrite(expireAfterWrite);
|
||||
// In tests, the cache duration is usually set to 0, which means the cache load synchronously
|
||||
// blocks every time it is called anyway because of the expireAfterWrite() above. Thus, setting
|
||||
// the refreshAfterWrite won't do anything, plus it's not legal to call it with a zero value
|
||||
// anyway (Caffeine allows expireAfterWrite to be zero but not refreshAfterWrite).
|
||||
if (!refreshAfterWrite.isZero()) {
|
||||
caffeine = caffeine.refreshAfterWrite(refreshAfterWrite);
|
||||
}
|
||||
return caffeine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,9 @@ import static com.google.common.collect.Maps.transformValues;
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
@@ -59,36 +57,33 @@ public class ModelUtils {
|
||||
|
||||
/** Caches all instance fields on an object, including non-public and inherited fields. */
|
||||
private static final LoadingCache<Class<?>, ImmutableMap<String, Field>> ALL_FIELDS_CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
CacheUtils.newCacheBuilder()
|
||||
.build(
|
||||
new CacheLoader<Class<?>, ImmutableMap<String, Field>>() {
|
||||
@Override
|
||||
public ImmutableMap<String, Field> load(Class<?> clazz) {
|
||||
Deque<Class<?>> hierarchy = new ArrayDeque<>();
|
||||
// Walk the hierarchy up to but not including ImmutableObject (to ignore
|
||||
// hashCode).
|
||||
for (; clazz != ImmutableObject.class; clazz = clazz.getSuperclass()) {
|
||||
// Add to the front, so that shadowed fields show up later in the list.
|
||||
// This will mean that getFieldValues will show the most derived value.
|
||||
hierarchy.addFirst(clazz);
|
||||
}
|
||||
Map<String, Field> fields = new LinkedHashMap<>();
|
||||
for (Class<?> hierarchyClass : hierarchy) {
|
||||
// Don't use hierarchyClass.getFields() because it only picks up public fields.
|
||||
for (Field field : hierarchyClass.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
fields.put(field.getName(), field);
|
||||
}
|
||||
clazz -> {
|
||||
Deque<Class<?>> hierarchy = new ArrayDeque<>();
|
||||
// Walk the hierarchy up to but not including ImmutableObject (to ignore
|
||||
// hashCode).
|
||||
for (; clazz != ImmutableObject.class; clazz = clazz.getSuperclass()) {
|
||||
// Add to the front, so that shadowed fields show up later in the list.
|
||||
// This will mean that getFieldValues will show the most derived value.
|
||||
hierarchy.addFirst(clazz);
|
||||
}
|
||||
Map<String, Field> fields = new LinkedHashMap<>();
|
||||
for (Class<?> hierarchyClass : hierarchy) {
|
||||
// Don't use hierarchyClass.getFields() because it only picks up public fields.
|
||||
for (Field field : hierarchyClass.getDeclaredFields()) {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
field.setAccessible(true);
|
||||
fields.put(field.getName(), field);
|
||||
}
|
||||
}
|
||||
return ImmutableMap.copyOf(fields);
|
||||
}
|
||||
return ImmutableMap.copyOf(fields);
|
||||
});
|
||||
|
||||
/** Lists all instance fields on an object, including non-public and inherited fields. */
|
||||
public static Map<String, Field> getAllFields(Class<?> clazz) {
|
||||
return ALL_FIELDS_CACHE.getUnchecked(clazz);
|
||||
return ALL_FIELDS_CACHE.get(clazz);
|
||||
}
|
||||
|
||||
/** Return a string representing the persisted schema of a type or enum. */
|
||||
|
||||
@@ -38,6 +38,7 @@ import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldState;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -111,6 +112,17 @@ public final class OteAccountBuilder {
|
||||
DateTime.parse("2030-03-01T00:00:00Z"),
|
||||
Money.of(CurrencyUnit.USD, 0));
|
||||
|
||||
/**
|
||||
* The default billing account map applied to all OT&E registrars.
|
||||
*
|
||||
* <p>This contains dummy values for USD and JPY so that OT&E registrars can be granted access
|
||||
* to all existing TLDs in sandbox. Note that OT&E is only on sandbox and thus these dummy
|
||||
* values will never be used in production (the only environment where real invoicing takes
|
||||
* place).
|
||||
*/
|
||||
public static final ImmutableMap<CurrencyUnit, String> DEFAULT_BILLING_ACCOUNT_MAP =
|
||||
ImmutableMap.of(CurrencyUnit.USD, "123", CurrencyUnit.JPY, "456");
|
||||
|
||||
private final ImmutableMap<String, String> registrarIdToTld;
|
||||
private final Registry sunriseTld;
|
||||
private final Registry gaTld;
|
||||
@@ -305,6 +317,7 @@ public final class OteAccountBuilder {
|
||||
.setTldStateTransitions(ImmutableSortedMap.of(START_OF_TIME, initialTldState))
|
||||
.setDnsWriters(ImmutableSet.of("VoidDnsWriter"))
|
||||
.setPremiumList(premiumList.get())
|
||||
.setTldType(TldType.TEST)
|
||||
.setRoidSuffix(
|
||||
String.format(
|
||||
"%S%X",
|
||||
@@ -334,6 +347,7 @@ public final class OteAccountBuilder {
|
||||
.setPhoneNumber("+1.2125550100")
|
||||
.setIcannReferralEmail("nightmare@registrar.test")
|
||||
.setState(Registrar.State.ACTIVE)
|
||||
.setBillingAccountMap(DEFAULT_BILLING_ACCOUNT_MAP)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,40 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
* This flag will be added to any {@link OneTime} events that are created via, e.g., an
|
||||
* automated process to expand {@link Recurring} events.
|
||||
*/
|
||||
SYNTHETIC
|
||||
SYNTHETIC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets of renewal price behaviors that can be applied to billing recurrences.
|
||||
*
|
||||
* <p>When a client renews a domain, they could be charged differently, depending on factors such
|
||||
* as the client type and the domain itself.
|
||||
*/
|
||||
public enum RenewalPriceBehavior {
|
||||
/**
|
||||
* This indicates the renewal price is the default price.
|
||||
*
|
||||
* <p>By default, if the domain is premium, then premium price will be used. Otherwise, the
|
||||
* standard price of the TLD will be used.
|
||||
*/
|
||||
DEFAULT,
|
||||
/**
|
||||
* This indicates the domain will be renewed at standard price even if it's a premium domain.
|
||||
*
|
||||
* <p>We chose to name this "NONPREMIUM" rather than simply "STANDARD" to avoid confusion
|
||||
* between "STANDARD" and "DEFAULT".
|
||||
*
|
||||
* <p>This price behavior is used with anchor tenants.
|
||||
*/
|
||||
NONPREMIUM,
|
||||
/**
|
||||
* This indicates that the renewalPrice in {@link BillingEvent.Recurring} will be used for
|
||||
* domain renewal.
|
||||
*
|
||||
* <p>The renewalPrice has a non-null value iff the price behavior is set to "SPECIFIED". This
|
||||
* behavior is used with internal registrations.
|
||||
*/
|
||||
SPECIFIED;
|
||||
}
|
||||
|
||||
/** Entity id. */
|
||||
@@ -555,6 +588,22 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
})
|
||||
TimeOfYear recurrenceTimeOfYear;
|
||||
|
||||
/**
|
||||
* The renewal price for domain renewal if and only if it's specified.
|
||||
*
|
||||
* <p>This price column remains null except when the renewal price behavior of the billing is
|
||||
* SPECIFIED. This column is used for internal registrations.
|
||||
*/
|
||||
@Nullable
|
||||
@Type(type = JodaMoneyType.TYPE_NAME)
|
||||
@Columns(
|
||||
columns = {@Column(name = "renewalPriceAmount"), @Column(name = "renewalPriceCurrency")})
|
||||
Money renewalPrice;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "renewalPriceBehavior", nullable = false)
|
||||
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
|
||||
|
||||
public DateTime getRecurrenceEndTime() {
|
||||
return recurrenceEndTime;
|
||||
}
|
||||
@@ -563,6 +612,14 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
return recurrenceTimeOfYear;
|
||||
}
|
||||
|
||||
public RenewalPriceBehavior getRenewalPriceBehavior() {
|
||||
return renewalPriceBehavior;
|
||||
}
|
||||
|
||||
public Optional<Money> getRenewalPrice() {
|
||||
return Optional.ofNullable(renewalPrice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VKey<Recurring> createVKey() {
|
||||
return VKey.create(Recurring.class, getId(), Key.create(this));
|
||||
@@ -591,11 +648,26 @@ public abstract class BillingEvent extends ImmutableObject
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPriceBehavior(RenewalPriceBehavior renewalPriceBehavior) {
|
||||
getInstance().renewalPriceBehavior = renewalPriceBehavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPrice(@Nullable Money renewalPrice) {
|
||||
getInstance().renewalPrice = renewalPrice;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recurring build() {
|
||||
Recurring instance = getInstance();
|
||||
checkNotNull(instance.eventTime);
|
||||
checkNotNull(instance.reason);
|
||||
checkArgument(
|
||||
(instance.renewalPriceBehavior == RenewalPriceBehavior.SPECIFIED)
|
||||
^ (instance.renewalPrice == null),
|
||||
"Renewal price can have a value if and only if the renewal price behavior is"
|
||||
+ " SPECIFIED");
|
||||
instance.recurrenceTimeOfYear = TimeOfYear.fromDateTime(instance.eventTime);
|
||||
instance.recurrenceEndTime =
|
||||
Optional.ofNullable(instance.recurrenceEndTime).orElse(END_OF_TIME);
|
||||
|
||||
+32
-20
@@ -18,18 +18,18 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
import google.registry.model.replay.SqlOnlyEntity;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.PersistenceException;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -62,11 +62,28 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
* not the phase is read-only.
|
||||
*/
|
||||
public enum MigrationState {
|
||||
/** Datastore is the only DB being used. */
|
||||
DATASTORE_ONLY(PrimaryDatabase.DATASTORE, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Datastore is the primary DB, with changes replicated to Cloud SQL. */
|
||||
DATASTORE_PRIMARY(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/** Datastore is the primary DB, with replication, and async actions are disallowed. */
|
||||
DATASTORE_PRIMARY_NO_ASYNC(PrimaryDatabase.DATASTORE, false, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/** Datastore is the primary DB, with replication, and all mutating actions are disallowed. */
|
||||
DATASTORE_PRIMARY_READ_ONLY(PrimaryDatabase.DATASTORE, true, ReplayDirection.DATASTORE_TO_SQL),
|
||||
|
||||
/**
|
||||
* Cloud SQL is the primary DB, with replication back to Datastore, and all mutating actions are
|
||||
* disallowed.
|
||||
*/
|
||||
SQL_PRIMARY_READ_ONLY(PrimaryDatabase.CLOUD_SQL, true, ReplayDirection.SQL_TO_DATASTORE),
|
||||
|
||||
/** Cloud SQL is the primary DB, with changes replicated to Datastore. */
|
||||
SQL_PRIMARY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.SQL_TO_DATASTORE),
|
||||
|
||||
/** Cloud SQL is the only DB being used. */
|
||||
SQL_ONLY(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
|
||||
private final PrimaryDatabase primaryDatabase;
|
||||
@@ -117,18 +134,8 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition>>
|
||||
// Each instance should cache the migration schedule for five minutes before reloading
|
||||
CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(Duration.ofMinutes(5))
|
||||
.build(
|
||||
new CacheLoader<
|
||||
Class<DatabaseMigrationStateSchedule>,
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition>>() {
|
||||
@Override
|
||||
public TimedTransitionProperty<MigrationState, MigrationStateTransition> load(
|
||||
Class<DatabaseMigrationStateSchedule> unused) {
|
||||
return DatabaseMigrationStateSchedule.getUncached();
|
||||
}
|
||||
});
|
||||
CacheUtils.newCacheBuilder(Duration.ofMinutes(5))
|
||||
.build(singletonClazz -> DatabaseMigrationStateSchedule.getUncached());
|
||||
|
||||
// Restrictions on the state transitions, e.g. no going from DATASTORE_ONLY to SQL_ONLY
|
||||
private static final ImmutableMultimap<MigrationState, MigrationState> VALID_STATE_TRANSITIONS =
|
||||
@@ -146,11 +153,17 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.putAll(
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY,
|
||||
MigrationState.DATASTORE_PRIMARY_NO_ASYNC,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY)
|
||||
.putAll(
|
||||
@@ -165,10 +178,9 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
MigrationState.SQL_ONLY,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY);
|
||||
|
||||
// In addition, we can always transition from a state to itself (useful when updating the map).
|
||||
for (MigrationState migrationState : MigrationState.values()) {
|
||||
builder.put(migrationState, migrationState);
|
||||
}
|
||||
Arrays.stream(MigrationState.values()).forEach(state -> builder.put(state, state));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -212,7 +224,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
|
||||
/** Loads the currently-set migration schedule from the cache, or the default if none exists. */
|
||||
public static TimedTransitionProperty<MigrationState, MigrationStateTransition> get() {
|
||||
return CACHE.getUnchecked(DatabaseMigrationStateSchedule.class);
|
||||
return CACHE.get(DatabaseMigrationStateSchedule.class);
|
||||
}
|
||||
|
||||
/** Returns the database migration status at the given time. */
|
||||
@@ -246,7 +258,7 @@ public class DatabaseMigrationStateSchedule extends CrossTldSingleton implements
|
||||
* A provided map of transitions may be valid by itself (i.e. it shifts states properly, doesn't
|
||||
* skip states, and doesn't backtrack incorrectly) while still being invalid. In addition to the
|
||||
* transitions in the map being valid, the single transition from the current map at the current
|
||||
* time to the new map at the current time time must also be valid.
|
||||
* time to the new map at the current time must also be valid.
|
||||
*/
|
||||
private static void validateTransitionAtCurrentTime(
|
||||
TimedTransitionProperty<MigrationState, MigrationStateTransition> newTransitions) {
|
||||
|
||||
@@ -175,7 +175,7 @@ public class DomainBase extends DomainContent
|
||||
|
||||
@Override
|
||||
public void beforeSqlSaveOnReplay() {
|
||||
fullyQualifiedDomainName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedDomainName);
|
||||
fullyQualifiedDomainName = DomainNameUtils.canonicalizeHostname(fullyQualifiedDomainName);
|
||||
dsData =
|
||||
dsData.stream()
|
||||
.filter(datum -> datum.getDigest() != null && datum.getDigest().length > 0)
|
||||
|
||||
@@ -34,7 +34,7 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.earliestOf;
|
||||
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static google.registry.util.DateTimeUtils.leapSafeAddYears;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
@@ -890,7 +890,7 @@ public class DomainContent extends EppResource
|
||||
|
||||
public B setDomainName(String domainName) {
|
||||
checkArgument(
|
||||
domainName.equals(canonicalizeDomainName(domainName)),
|
||||
domainName.equals(canonicalizeHostname(domainName)),
|
||||
"Domain name %s not in puny-coded, lower-case form",
|
||||
domainName);
|
||||
getInstance().fullyQualifiedDomainName = domainName;
|
||||
|
||||
@@ -309,7 +309,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity {
|
||||
if (domainContent == null) {
|
||||
domainContent = jpaTm().getEntityManager().find(DomainBase.class, getDomainRepoId());
|
||||
domainContent.fullyQualifiedDomainName =
|
||||
DomainNameUtils.canonicalizeDomainName(domainContent.fullyQualifiedDomainName);
|
||||
DomainNameUtils.canonicalizeHostname(domainContent.fullyQualifiedDomainName);
|
||||
fillAuxiliaryFieldsFromDomain(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import google.registry.model.BackupGroupRoot;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.annotations.ReportedOn;
|
||||
import google.registry.model.billing.BillingEvent.RenewalPriceBehavior;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimeMapper;
|
||||
import google.registry.model.common.TimedTransitionProperty.TimedTransition;
|
||||
@@ -151,6 +152,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
@Enumerated(EnumType.STRING)
|
||||
TokenType tokenType;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "renewalPriceBehavior", nullable = false)
|
||||
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
|
||||
|
||||
// TODO: Remove onLoad once all allocation tokens are migrated to have a discountYears of 1.
|
||||
@OnLoad
|
||||
void onLoad() {
|
||||
@@ -240,6 +245,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
return tokenStatusTransitions;
|
||||
}
|
||||
|
||||
public RenewalPriceBehavior getRenewalPriceBehavior() {
|
||||
return renewalPriceBehavior;
|
||||
}
|
||||
|
||||
public VKey<AllocationToken> createVKey() {
|
||||
return VKey.create(AllocationToken.class, getToken(), Key.create(this));
|
||||
}
|
||||
@@ -362,5 +371,10 @@ public class AllocationToken extends BackupGroupRoot implements Buildable, Datas
|
||||
"tokenStatusTransitions must start with NOT_STARTED");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRenewalPriceBehavior(RenewalPriceBehavior renewalPriceBehavior) {
|
||||
getInstance().renewalPriceBehavior = renewalPriceBehavior;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.googlecode.objectify.annotation.IgnoreSave;
|
||||
@@ -195,7 +195,7 @@ public class HostBase extends EppResource {
|
||||
|
||||
public B setHostName(String hostName) {
|
||||
checkArgument(
|
||||
hostName.equals(canonicalizeDomainName(hostName)),
|
||||
hostName.equals(canonicalizeHostname(hostName)),
|
||||
"Host name %s not in puny-coded, lower-case form",
|
||||
hostName);
|
||||
getInstance().fullyQualifiedHostName = hostName;
|
||||
|
||||
@@ -143,7 +143,7 @@ public class HostHistory extends HistoryEntry implements SqlEntity, UnsafeSerial
|
||||
if (hostBase == null) {
|
||||
hostBase = jpaTm().getEntityManager().find(HostResource.class, getHostRepoId());
|
||||
hostBase.fullyQualifiedHostName =
|
||||
DomainNameUtils.canonicalizeDomainName(hostBase.fullyQualifiedHostName);
|
||||
DomainNameUtils.canonicalizeHostname(hostBase.fullyQualifiedHostName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ public class HostResource extends HostBase
|
||||
|
||||
@Override
|
||||
public void beforeSqlSaveOnReplay() {
|
||||
fullyQualifiedHostName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedHostName);
|
||||
fullyQualifiedHostName = DomainNameUtils.canonicalizeHostname(fullyQualifiedHostName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -69,7 +69,7 @@ public class CommitLogMutation extends ImmutableObject implements DatastoreOnlyE
|
||||
* converted to a raw Datastore Entity, serialized to bytes, and stored within the mutation.
|
||||
*/
|
||||
public static CommitLogMutation create(Key<CommitLogManifest> parent, Object entity) {
|
||||
return createFromRaw(parent, auditedOfy().save().toEntity(entity));
|
||||
return createFromRaw(parent, auditedOfy().saveIgnoringReadOnlyWithBackup().toEntity(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -156,7 +156,7 @@ public class CommitLoggedWork<R> implements Runnable {
|
||||
.map(entity -> (ImmutableObject) CommitLogMutation.create(manifestKey, entity))
|
||||
.collect(toImmutableSet());
|
||||
auditedOfy()
|
||||
.saveWithoutBackup()
|
||||
.saveIgnoringReadOnlyWithoutBackup()
|
||||
.entities(
|
||||
new ImmutableSet.Builder<>()
|
||||
.add(manifest)
|
||||
|
||||
@@ -81,6 +81,7 @@ import google.registry.model.common.EntityGroupRoot;
|
||||
import google.registry.model.registrar.Registrar.BillingAccountEntry.CurrencyMapper;
|
||||
import google.registry.model.replay.DatastoreAndSqlEntity;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.model.tld.Registry.TldType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
@@ -798,13 +799,9 @@ public class Registrar extends ImmutableObject
|
||||
}
|
||||
|
||||
public Builder setBillingAccountMap(@Nullable Map<CurrencyUnit, String> billingAccountMap) {
|
||||
if (billingAccountMap == null) {
|
||||
getInstance().billingAccountMap = null;
|
||||
} else {
|
||||
getInstance().billingAccountMap =
|
||||
billingAccountMap.entrySet().stream()
|
||||
.collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new));
|
||||
}
|
||||
getInstance().billingAccountMap =
|
||||
nullToEmptyImmutableCopy(billingAccountMap).entrySet().stream()
|
||||
.collect(toImmutableMap(Map.Entry::getKey, BillingAccountEntry::new));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1015,6 +1012,20 @@ public class Registrar extends ImmutableObject
|
||||
String.format(
|
||||
"Supplied IANA ID is not valid for %s registrar type: %s",
|
||||
getInstance().type, getInstance().ianaIdentifier));
|
||||
|
||||
// In order to grant access to real TLDs, the registrar must have a corresponding billing
|
||||
// account ID for that TLD's billing currency.
|
||||
ImmutableSet<String> nonBillableTlds =
|
||||
Registry.get(getInstance().getAllowedTlds()).stream()
|
||||
.filter(r -> r.getTldType() == TldType.REAL)
|
||||
.filter(r -> !getInstance().getBillingAccountMap().containsKey(r.getCurrency()))
|
||||
.map(Registry::getTldStr)
|
||||
.collect(toImmutableSet());
|
||||
checkArgument(
|
||||
nonBillableTlds.isEmpty(),
|
||||
"Cannot set these allowed, real TLDs because their currency is missing "
|
||||
+ "from the billing account map: %s",
|
||||
nonBillableTlds);
|
||||
return cloneEmptyToNull(super.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,14 @@ package google.registry.model.server;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.Ignore;
|
||||
import com.googlecode.objectify.annotation.OnLoad;
|
||||
import com.googlecode.objectify.annotation.Unindex;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.annotations.NotBackedUp;
|
||||
import google.registry.model.annotations.NotBackedUp.Reason;
|
||||
import google.registry.model.common.CrossTldSingleton;
|
||||
@@ -32,7 +31,6 @@ import google.registry.model.replay.NonReplicatedEntity;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.PostLoad;
|
||||
import javax.persistence.Transient;
|
||||
@@ -52,14 +50,7 @@ public class ServerSecret extends CrossTldSingleton implements NonReplicatedEnti
|
||||
* Supplier that can be reset for testing purposes.
|
||||
*/
|
||||
private static final LoadingCache<Class<ServerSecret>, ServerSecret> CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.build(
|
||||
new CacheLoader<Class<ServerSecret>, ServerSecret>() {
|
||||
@Override
|
||||
public ServerSecret load(Class<ServerSecret> unused) {
|
||||
return retrieveAndSaveSecret();
|
||||
}
|
||||
});
|
||||
CacheUtils.newCacheBuilder().build(singletonClazz -> retrieveAndSaveSecret());
|
||||
|
||||
private static ServerSecret retrieveAndSaveSecret() {
|
||||
if (tm().isOfy()) {
|
||||
@@ -84,11 +75,7 @@ public class ServerSecret extends CrossTldSingleton implements NonReplicatedEnti
|
||||
|
||||
/** Returns the global ServerSecret instance, creating it if one isn't already in Datastore. */
|
||||
public static ServerSecret get() {
|
||||
try {
|
||||
return CACHE.get(ServerSecret.class);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return CACHE.get(ServerSecret.class);
|
||||
}
|
||||
|
||||
/** Most significant 8 bytes of the UUID value (stored separately for legacy purposes). */
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class Registries {
|
||||
.stream()
|
||||
.map(Key::getName)
|
||||
.collect(toImmutableSet());
|
||||
return Registry.getAll(tlds).stream()
|
||||
return Registry.get(tlds).stream()
|
||||
.map(e -> Maps.immutableEntry(e.getTldStr(), e.getTldType()))
|
||||
.collect(entriesToImmutableMap());
|
||||
} else {
|
||||
@@ -105,7 +105,7 @@ public final class Registries {
|
||||
|
||||
/** Returns the Registry entities themselves of the given type loaded fresh from Datastore. */
|
||||
public static ImmutableSet<Registry> getTldEntitiesOfType(TldType type) {
|
||||
return Registry.getAll(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
return Registry.get(filterValues(cache.get(), equalTo(type)).keySet());
|
||||
}
|
||||
|
||||
/** Pass-through check that the specified TLD exists, otherwise throw an IAE. */
|
||||
|
||||
@@ -142,10 +142,16 @@ public class Registry extends ImmutableObject
|
||||
|
||||
/** The type of TLD, which determines things like backups and escrow policy. */
|
||||
public enum TldType {
|
||||
/** A real, official TLD. */
|
||||
/**
|
||||
* A real, official TLD (but not necessarily only on production).
|
||||
*
|
||||
* <p>Note that, to avoid unnecessary costly DB writes, {@link
|
||||
* google.registry.model.reporting.DomainTransactionRecord}s are only written out for REAL TLDs
|
||||
* (these transaction records are only used for ICANN reporting purposes).
|
||||
*/
|
||||
REAL,
|
||||
|
||||
/** A test TLD, for the prober. */
|
||||
/** A test TLD, for the prober, OT&E, and other testing purposes. */
|
||||
TEST
|
||||
}
|
||||
|
||||
@@ -232,7 +238,7 @@ public class Registry extends ImmutableObject
|
||||
}
|
||||
|
||||
/** Returns the registry entities for the given TLD strings, throwing if any don't exist. */
|
||||
static ImmutableSet<Registry> getAll(Set<String> tlds) {
|
||||
public static ImmutableSet<Registry> get(Set<String> tlds) {
|
||||
try {
|
||||
ImmutableMap<String, Optional<Registry>> registries = CACHE.getAll(tlds);
|
||||
ImmutableSet<String> missingRegistries =
|
||||
@@ -264,8 +270,7 @@ public class Registry extends ImmutableObject
|
||||
/** A cache that loads the {@link Registry} for a given tld. */
|
||||
private static final LoadingCache<String, Optional<Registry>> CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(
|
||||
java.time.Duration.ofMillis(getSingletonCacheRefreshDuration().getMillis()))
|
||||
.expireAfterWrite(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<String, Optional<Registry>>() {
|
||||
@Override
|
||||
@@ -1001,7 +1006,7 @@ public class Registry extends ImmutableObject
|
||||
instance.renewBillingCostTransitions.checkValidity();
|
||||
instance.eapFeeSchedule.checkValidity();
|
||||
// All costs must be in the expected currency.
|
||||
// TODO(b/21854155): When we move PremiumList into Datastore, verify its currency too.
|
||||
checkArgumentNotNull(instance.getCurrency(), "Currency must be set");
|
||||
checkArgument(
|
||||
instance.getStandardCreateCost().getCurrencyUnit().equals(instance.currency),
|
||||
"Create cost must be in the registry's currency");
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.model.tld.label;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -77,7 +77,7 @@ public abstract class DomainLabelEntry<T extends Comparable<?>, D extends Domain
|
||||
public T build() {
|
||||
checkArgumentNotNull(emptyToNull(getInstance().domainLabel), "Label must be specified");
|
||||
checkArgument(
|
||||
getInstance().domainLabel.equals(canonicalizeDomainName(getInstance().domainLabel)),
|
||||
getInstance().domainLabel.equals(canonicalizeHostname(getInstance().domainLabel)),
|
||||
"Label '%s' must be in puny-coded, lower-case form",
|
||||
getInstance().domainLabel);
|
||||
checkArgumentNotNull(getInstance().getValue(), "Value must be specified");
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.batch.ExpandRecurringBillingEventsAction;
|
||||
import google.registry.batch.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesPipelineAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
import google.registry.batch.WipeOutCloudSqlAction;
|
||||
@@ -196,6 +197,8 @@ interface BackendRequestComponent {
|
||||
|
||||
ResaveAllEppResourcesAction resaveAllEppResourcesAction();
|
||||
|
||||
ResaveAllEppResourcesPipelineAction resaveAllEppResourcesPipelineAction();
|
||||
|
||||
ResaveEntityAction resaveEntityAction();
|
||||
|
||||
SendExpiringCertificateNotificationEmailAction sendExpiringCertificateNotificationEmailAction();
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nullable;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
@@ -116,20 +117,28 @@ public class JodaMoneyType implements CompositeUserType {
|
||||
return Objects.hashCode(x);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object nullSafeGet(
|
||||
ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException, SQLException {
|
||||
BigDecimal amount = StandardBasicTypes.BIG_DECIMAL.nullSafeGet(rs, names[AMOUNT_ID], session);
|
||||
CurrencyUnit currencyUnit =
|
||||
CurrencyUnit.of(StandardBasicTypes.STRING.nullSafeGet(rs, names[CURRENCY_ID], session));
|
||||
if (amount != null && currencyUnit != null) {
|
||||
return Money.of(currencyUnit, amount.stripTrailingZeros());
|
||||
}
|
||||
if (amount == null && currencyUnit == null) {
|
||||
String currencyUnitString =
|
||||
StandardBasicTypes.STRING.nullSafeGet(rs, names[CURRENCY_ID], session);
|
||||
// It is allowable for a Money object to be null, but only if both the currency unit and the
|
||||
// amount are null
|
||||
if (amount == null && currencyUnitString == null) {
|
||||
return null;
|
||||
} else if (amount != null && currencyUnitString != null) {
|
||||
// CurrencyUnit.of() throws an IllegalCurrencyException for unknown currency, which means the
|
||||
// currency is valid if it returns a value
|
||||
return Money.of(CurrencyUnit.of(currencyUnitString), amount.stripTrailingZeros());
|
||||
} else {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
"Mismatching null state between currency '%s' and amount '%s'",
|
||||
currencyUnitString, amount));
|
||||
}
|
||||
throw new HibernateException("Mismatching null state between currency and amount.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,7 +149,7 @@ public class JodaMoneyType implements CompositeUserType {
|
||||
String currencyUnit = value == null ? null : ((Money) value).getCurrencyUnit().getCode();
|
||||
|
||||
if ((amount == null && currencyUnit != null) || (amount != null && currencyUnit == null)) {
|
||||
throw new HibernateException("Mismatching null state between currency and amount.");
|
||||
throw new HibernateException("Mismatching null state between currency and amount");
|
||||
}
|
||||
StandardBasicTypes.BIG_DECIMAL.nullSafeSet(st, amount, index, session);
|
||||
StandardBasicTypes.STRING.nullSafeSet(st, currencyUnit, index + 1, session);
|
||||
|
||||
@@ -33,8 +33,10 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamClass;
|
||||
|
||||
/**
|
||||
* A SQL transaction that can be serialized and stored in its own table.
|
||||
@@ -105,7 +107,8 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
}
|
||||
|
||||
public static Transaction deserialize(byte[] serializedTransaction) throws IOException {
|
||||
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(serializedTransaction));
|
||||
ObjectInputStream in =
|
||||
new LenientObjectInputStream(new ByteArrayInputStream(serializedTransaction));
|
||||
|
||||
// Verify that the data is what we expect.
|
||||
int version = in.readInt();
|
||||
@@ -304,4 +307,35 @@ public class Transaction extends ImmutableObject implements Buildable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ObjectInputStream that ignores the UIDs of serialized objects.
|
||||
*
|
||||
* <p>We only really need to deserialize VKeys. However, VKeys have a class object associated with
|
||||
* them, and if the class is changed and we haven't defined a serialVersionUID for it, we get an
|
||||
* exception during deserialization.
|
||||
*
|
||||
* <p>It's safe for us to ignore this condition: we only care about attaching the correct local
|
||||
* class object to the VKey. So this class effectively does so by replacing the class descriptor
|
||||
* if it's version UID doesn't match that of the local class.
|
||||
*/
|
||||
private static class LenientObjectInputStream extends ObjectInputStream {
|
||||
|
||||
public LenientObjectInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
|
||||
ObjectStreamClass persistedDescriptor = super.readClassDescriptor();
|
||||
Class localClass = Class.forName(persistedDescriptor.getName());
|
||||
ObjectStreamClass localDescriptor = ObjectStreamClass.lookup(localClass);
|
||||
if (localDescriptor != null) {
|
||||
if (persistedDescriptor.getSerialVersionUID() != localDescriptor.getSerialVersionUID()) {
|
||||
return localDescriptor;
|
||||
}
|
||||
}
|
||||
return persistedDescriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-4
@@ -15,23 +15,25 @@
|
||||
package google.registry.persistence.transaction;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState.DATASTORE_PRIMARY_NO_ASYNC;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.appengine.api.utils.SystemProperty;
|
||||
import com.google.appengine.api.utils.SystemProperty.Environment.Value;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Suppliers;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.PrimaryDatabase;
|
||||
import google.registry.model.ofy.DatastoreTransactionManager;
|
||||
import google.registry.persistence.DaggerPersistenceComponent;
|
||||
import google.registry.tools.RegistryToolEnvironment;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import google.registry.util.SystemClock;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Factory class to create {@link TransactionManager} instance. */
|
||||
// TODO: Rename this to PersistenceFactory and move to persistence package.
|
||||
@@ -42,6 +44,9 @@ public final class TransactionManagerFactory {
|
||||
/** Optional override to manually set the transaction manager per-test. */
|
||||
private static Optional<TransactionManager> tmForTest = Optional.empty();
|
||||
|
||||
/** The current clock (defined as a variable so we can override it in tests) */
|
||||
private static Clock clock = new SystemClock();
|
||||
|
||||
/** Supplier for jpaTm so that it is initialized only once, upon first usage. */
|
||||
@NonFinalForTesting
|
||||
private static Supplier<JpaTransactionManager> jpaTm =
|
||||
@@ -103,7 +108,7 @@ public final class TransactionManagerFactory {
|
||||
if (onBeam) {
|
||||
return jpaTm();
|
||||
}
|
||||
return DatabaseMigrationStateSchedule.getValueAtTime(DateTime.now(UTC))
|
||||
return DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc())
|
||||
.getPrimaryDatabase()
|
||||
.equals(PrimaryDatabase.DATASTORE)
|
||||
? ofyTm()
|
||||
@@ -193,11 +198,33 @@ public final class TransactionManagerFactory {
|
||||
}
|
||||
|
||||
public static void assertNotReadOnlyMode() {
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(DateTime.now(UTC)).isReadOnly()) {
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc()).isReadOnly()) {
|
||||
throw new ReadOnlyModeException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that async actions (contact/host deletes and host renames) are allowed.
|
||||
*
|
||||
* <p>These are allowed at all times except during the {@link
|
||||
* DatabaseMigrationStateSchedule.MigrationState#DATASTORE_PRIMARY_NO_ASYNC} stage. Note that
|
||||
* {@link ReadOnlyModeException} may well be thrown during other read-only stages inside the
|
||||
* transaction manager; this method specifically checks only async actions.
|
||||
*/
|
||||
@DeleteAfterMigration
|
||||
public static void assertAsyncActionsAreAllowed() {
|
||||
if (DatabaseMigrationStateSchedule.getValueAtTime(clock.nowUtc())
|
||||
.equals(DATASTORE_PRIMARY_NO_ASYNC)) {
|
||||
throw new ReadOnlyModeException();
|
||||
}
|
||||
}
|
||||
|
||||
/** Allows us to set the clock used by the factory in unit tests. */
|
||||
@VisibleForTesting
|
||||
public static void setClockForTesting(Clock clock) {
|
||||
TransactionManagerFactory.clock = clock;
|
||||
}
|
||||
|
||||
/** Registry is currently undergoing maintenance and is in read-only mode. */
|
||||
public static class ReadOnlyModeException extends IllegalStateException {
|
||||
public ReadOnlyModeException() {
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Charsets.UTF_8;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN;
|
||||
import static google.registry.request.Actions.getPathForAction;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
@@ -245,7 +245,7 @@ public abstract class RdapActionBase implements Runnable {
|
||||
}
|
||||
|
||||
String canonicalizeName(String name) {
|
||||
name = canonicalizeDomainName(name);
|
||||
name = canonicalizeHostname(name);
|
||||
if (name.endsWith(".")) {
|
||||
name = name.substring(0, name.length() - 1);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.google.appengine.api.urlfetch.HTTPMethod.PUT;
|
||||
import static com.google.common.io.BaseEncoding.base64;
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
@@ -123,7 +123,7 @@ public class RdeReporter {
|
||||
|
||||
private URL makeReportUrl(String tld, String id) {
|
||||
try {
|
||||
return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeDomainName(tld), id));
|
||||
return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeHostname(tld), id));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public final class RdeUploadAction implements Runnable, EscrowTask {
|
||||
// If a prefix is not provided, but we are in SQL mode, try to determine the prefix. This should
|
||||
// only happen when the RDE upload cron job runs to catch up any un-retried (i. e. expected)
|
||||
// RDE failures.
|
||||
if (prefix.isEmpty() && !tm().isOfy()) {
|
||||
if (!prefix.isPresent() && !tm().isOfy()) {
|
||||
// The prefix is always in the format of: rde-2022-02-21t00-00-00z-2022-02-21t00-07-33z, where
|
||||
// the first datetime is the watermark and the second one is the time when the RDE beam job
|
||||
// launched. We search for the latest folder that starts with "rde-[watermark]".
|
||||
|
||||
@@ -17,11 +17,11 @@ package google.registry.tmch;
|
||||
import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PILOT;
|
||||
import static google.registry.config.RegistryConfig.ConfigModule.TmchCaMode.PRODUCTION;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static google.registry.model.CacheUtils.newCacheBuilder;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
|
||||
@@ -76,9 +76,7 @@ public final class TmchCertificateAuthority {
|
||||
* persist the correct one for this given environment.
|
||||
*/
|
||||
private static final LoadingCache<TmchCaMode, X509CRL> CRL_CACHE =
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(
|
||||
java.time.Duration.ofMillis(getSingletonCacheRefreshDuration().getMillis()))
|
||||
newCacheBuilder(getSingletonCacheRefreshDuration())
|
||||
.build(
|
||||
new CacheLoader<TmchCaMode, X509CRL>() {
|
||||
@Override
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
@@ -80,7 +80,7 @@ final class CanonicalizeLabelsCommand implements Command {
|
||||
|
||||
private String canonicalize(String rawLabel) {
|
||||
try {
|
||||
return canonicalizeDomainName(rawLabel.replaceAll(" ", ""));
|
||||
return canonicalizeHostname(rawLabel.replaceAll(" ", ""));
|
||||
} catch (Exception e) {
|
||||
System.err.printf("Error canonicalizing %s: %s\n", rawLabel, e.getMessage());
|
||||
return "";
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.ACE_PREFIX;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -38,7 +38,7 @@ final class ConvertIdnCommand implements Command {
|
||||
if (label.startsWith(ACE_PREFIX)) {
|
||||
System.out.println(Idn.toUnicode(Ascii.toLowerCase(label)));
|
||||
} else {
|
||||
System.out.println(canonicalizeDomainName(label));
|
||||
System.out.println(canonicalizeHostname(label));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.tools.soy.HostCreateSoyInfo;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
@@ -68,7 +69,7 @@ final class CreateHostCommand extends MutatingEppToolCommand {
|
||||
addSoyRecord(
|
||||
clientId,
|
||||
new SoyMapData(
|
||||
"hostname", hostName,
|
||||
"hostname", DomainNameUtils.canonicalizeHostname(hostName),
|
||||
"ipv4addresses", ipv4Addresses.build(),
|
||||
"ipv6addresses", ipv6Addresses.build()));
|
||||
}
|
||||
|
||||
@@ -18,20 +18,17 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Predicates.isNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
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.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.tld.Registry;
|
||||
import google.registry.tools.params.KeyValueMapParameter.CurrencyUnitToStringMap;
|
||||
import google.registry.tools.params.OptionalLongParameter;
|
||||
import google.registry.tools.params.OptionalPhoneNumberParameter;
|
||||
@@ -46,7 +43,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
@@ -319,7 +315,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
addAllowedTlds.isEmpty(), "Can't specify both --allowedTlds and --addAllowedTlds");
|
||||
ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>();
|
||||
for (String allowedTld : allowedTlds) {
|
||||
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
|
||||
allowedTldsBuilder.add(canonicalizeHostname(allowedTld));
|
||||
}
|
||||
builder.setAllowedTlds(allowedTldsBuilder.build());
|
||||
}
|
||||
@@ -329,7 +325,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
allowedTldsBuilder.addAll(oldRegistrar.getAllowedTlds());
|
||||
}
|
||||
for (String allowedTld : addAllowedTlds) {
|
||||
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
|
||||
allowedTldsBuilder.add(canonicalizeHostname(allowedTld));
|
||||
}
|
||||
builder.setAllowedTlds(allowedTldsBuilder.build());
|
||||
}
|
||||
@@ -433,22 +429,6 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
// Require a phone passcode.
|
||||
checkArgument(
|
||||
newRegistrar.getPhonePasscode() != null, "--passcode is required for REAL registrars.");
|
||||
// Check if registrar has billing account IDs for the currency of the TLDs that it is
|
||||
// allowed to register.
|
||||
ImmutableSet<CurrencyUnit> tldCurrencies =
|
||||
newRegistrar
|
||||
.getAllowedTlds()
|
||||
.stream()
|
||||
.map(tld -> Registry.get(tld).getCurrency())
|
||||
.collect(toImmutableSet());
|
||||
Set<CurrencyUnit> currenciesWithoutBillingAccountId =
|
||||
newRegistrar.getBillingAccountMap() == null
|
||||
? tldCurrencies
|
||||
: Sets.difference(tldCurrencies, newRegistrar.getBillingAccountMap().keySet());
|
||||
checkArgument(
|
||||
currenciesWithoutBillingAccountId.isEmpty(),
|
||||
"Need billing account map entries for currencies: %s",
|
||||
Joiner.on(' ').join(currenciesWithoutBillingAccountId));
|
||||
}
|
||||
|
||||
stageEntityChange(oldRegistrar, newRegistrar);
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.util.CollectionUtils.findDuplicates;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Joiner;
|
||||
@@ -263,10 +263,10 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
"Can't update roid suffixes on multiple TLDs simultaneously");
|
||||
for (String tld : tlds) {
|
||||
checkArgument(
|
||||
tld.equals(canonicalizeDomainName(tld)),
|
||||
tld.equals(canonicalizeHostname(tld)),
|
||||
"TLD '%s' should be given in the canonical form '%s'",
|
||||
tld,
|
||||
canonicalizeDomainName(tld));
|
||||
canonicalizeHostname(tld));
|
||||
checkArgument(
|
||||
!Character.isDigit(tld.charAt(0)),
|
||||
"TLDs cannot begin with a number");
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.tools.soy.HostDeleteSoyInfo;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
|
||||
/** A command to delete a host via EPP. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete host")
|
||||
@@ -50,9 +51,11 @@ final class DeleteHostCommand extends MutatingEppToolCommand {
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
setSoyTemplate(HostDeleteSoyInfo.getInstance(), HostDeleteSoyInfo.DELETEHOST);
|
||||
addSoyRecord(clientId, new SoyMapData(
|
||||
"hostName", hostName,
|
||||
"reason", reason,
|
||||
"requestedByRegistrar", requestedByRegistrar));
|
||||
addSoyRecord(
|
||||
clientId,
|
||||
new SoyMapData(
|
||||
"hostName", DomainNameUtils.canonicalizeHostname(hostName),
|
||||
"reason", reason,
|
||||
"requestedByRegistrar", requestedByRegistrar));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
@@ -61,6 +61,6 @@ class EncryptEscrowDepositCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Override
|
||||
public final void run() throws Exception {
|
||||
encryptor.encrypt(mode, canonicalizeDomainName(tld), revision, input, outdir);
|
||||
encryptor.encrypt(mode, canonicalizeHostname(tld), revision, input, outdir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -79,6 +80,7 @@ class EnqueuePollMessageCommand extends MutatingCommand {
|
||||
|
||||
@Override
|
||||
protected final void init() {
|
||||
domainName = DomainNameUtils.canonicalizeHostname(domainName);
|
||||
checkArgument(
|
||||
!sendToAll || isNullOrEmpty(clientIds), "Cannot specify both --all and --clients");
|
||||
tm().transact(
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.google.common.collect.Maps.filterValues;
|
||||
import static com.google.common.io.Resources.getResource;
|
||||
import static google.registry.model.tld.Registries.findTldForNameOrThrow;
|
||||
import static google.registry.tools.CommandUtilities.addHeader;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static google.registry.xml.XmlTransformer.prettyPrint;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@@ -83,8 +84,9 @@ abstract class EppToolCommand extends ConfirmingCommand
|
||||
protected static Multimap<String, String> validateAndGroupDomainNamesByTld(List<String> names) {
|
||||
ImmutableMultimap.Builder<String, String> builder = new ImmutableMultimap.Builder<>();
|
||||
for (String name : names) {
|
||||
InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(name));
|
||||
builder.put(tld.toString(), name);
|
||||
String canonicalDomain = canonicalizeHostname(name);
|
||||
InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(canonicalDomain));
|
||||
builder.put(tld.toString(), canonicalDomain);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Queues.newArrayDeque;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
@@ -44,15 +43,18 @@ import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import google.registry.util.Retrier;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -164,11 +166,12 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi {
|
||||
domainNames = null;
|
||||
} else {
|
||||
domainNames =
|
||||
newArrayDeque(
|
||||
Splitter.on('\n')
|
||||
.omitEmptyStrings()
|
||||
.trimResults()
|
||||
.split(Files.asCharSource(new File(domainNamesFile), UTF_8).read()));
|
||||
Splitter.on('\n')
|
||||
.omitEmptyStrings()
|
||||
.trimResults()
|
||||
.splitToStream(Files.asCharSource(new File(domainNamesFile), UTF_8).read())
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.collect(Collectors.toCollection(ArrayDeque::new));
|
||||
numTokens = domainNames.size();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a domain resource. */
|
||||
@@ -33,8 +34,11 @@ final class GetDomainCommand extends GetEppResourceCommand {
|
||||
@Override
|
||||
public void runAndPrint() {
|
||||
for (String domainName : mainParameters) {
|
||||
String canonicalDomain = DomainNameUtils.canonicalizeHostname(domainName);
|
||||
printResource(
|
||||
"Domain", domainName, loadByForeignKey(DomainBase.class, domainName, readTimestamp));
|
||||
"Domain",
|
||||
canonicalDomain,
|
||||
loadByForeignKey(DomainBase.class, canonicalDomain, readTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show one or more host resources. */
|
||||
@@ -32,7 +33,9 @@ final class GetHostCommand extends GetEppResourceCommand {
|
||||
|
||||
@Override
|
||||
public void runAndPrint() {
|
||||
mainParameters.forEach(
|
||||
h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp)));
|
||||
mainParameters.stream()
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.forEach(
|
||||
h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/**
|
||||
@@ -26,6 +28,6 @@ public class LockDomainCommand extends LockOrUnlockDomainCommand {
|
||||
|
||||
@Override
|
||||
protected void createAndApplyRequest(String domain) {
|
||||
domainLockUtils.administrativelyApplyLock(domain, clientId, null, true);
|
||||
domainLockUtils.administrativelyApplyLock(canonicalizeHostname(domain), clientId, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ import java.io.ByteArrayInputStream;
|
||||
import java.net.URL;
|
||||
import java.security.Security;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.postgresql.util.PSQLException;
|
||||
|
||||
/** Container class to create and run remote commands against a Datastore instance. */
|
||||
@Parameters(separators = " =", commandDescription = "Command-line interface to the registry")
|
||||
@@ -178,14 +180,30 @@ final class RegistryCli implements AutoCloseable, CommandRunner {
|
||||
|
||||
try {
|
||||
runCommand(command);
|
||||
} catch (RuntimeException ex) {
|
||||
if (Throwables.getRootCause(ex) instanceof LoginRequiredException) {
|
||||
} catch (RuntimeException e) {
|
||||
if (Throwables.getRootCause(e) instanceof LoginRequiredException) {
|
||||
System.err.println("===================================================================");
|
||||
System.err.println("You must login using 'nomulus login' prior to running this command.");
|
||||
System.err.println("===================================================================");
|
||||
System.exit(1);
|
||||
} else {
|
||||
throw ex;
|
||||
// See if this looks like the error we get when there's another instance of nomulus tool
|
||||
// running against SQL and give the user some additional guidance if so.
|
||||
Optional<Throwable> psqlException =
|
||||
Throwables.getCausalChain(e).stream()
|
||||
.filter(x -> x instanceof PSQLException)
|
||||
.findFirst();
|
||||
if (psqlException.isPresent() && psqlException.get().getMessage().contains("google:5432")) {
|
||||
e.printStackTrace();
|
||||
System.err.println("===================================================================");
|
||||
System.err.println(
|
||||
"This error is likely the result of having another instance of\n"
|
||||
+ "nomulus running at the same time. Check your system, shut down\n"
|
||||
+ "the other instance, and try again.");
|
||||
System.err.println("===================================================================");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.tools.javascrap.BackfillRegistrarBillingAccountsCommand;
|
||||
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
|
||||
import google.registry.tools.javascrap.HardDeleteHostCommand;
|
||||
|
||||
@@ -30,6 +31,7 @@ public final class RegistryTool {
|
||||
public static final ImmutableMap<String, Class<? extends Command>> COMMAND_MAP =
|
||||
new ImmutableMap.Builder<String, Class<? extends Command>>()
|
||||
.put("ack_poll_messages", AckPollMessagesCommand.class)
|
||||
.put("backfill_registrar_billing_accounts", BackfillRegistrarBillingAccountsCommand.class)
|
||||
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
|
||||
.put("check_domain", CheckDomainCommand.class)
|
||||
.put("check_domain_claims", CheckDomainClaimsCommand.class)
|
||||
@@ -102,6 +104,7 @@ public final class RegistryTool {
|
||||
.put("registrar_contact", RegistrarContactCommand.class)
|
||||
.put("remove_registry_one_key", RemoveRegistryOneKeyCommand.class)
|
||||
.put("renew_domain", RenewDomainCommand.class)
|
||||
.put("replay_txns", ReplayTxnsCommand.class)
|
||||
.put("resave_entities", ResaveEntitiesCommand.class)
|
||||
.put("resave_environment_entities", ResaveEnvironmentEntitiesCommand.class)
|
||||
.put("resave_epp_resource", ResaveEppResourceCommand.class)
|
||||
|
||||
@@ -43,6 +43,7 @@ import google.registry.request.Modules.UrlConnectionServiceModule;
|
||||
import google.registry.request.Modules.UrlFetchServiceModule;
|
||||
import google.registry.request.Modules.UserServiceModule;
|
||||
import google.registry.tools.AuthModule.LocalCredentialModule;
|
||||
import google.registry.tools.javascrap.BackfillRegistrarBillingAccountsCommand;
|
||||
import google.registry.tools.javascrap.CompareEscrowDepositsCommand;
|
||||
import google.registry.tools.javascrap.HardDeleteHostCommand;
|
||||
import google.registry.util.UtilsModule;
|
||||
@@ -90,6 +91,8 @@ import javax.inject.Singleton;
|
||||
interface RegistryToolComponent {
|
||||
void inject(AckPollMessagesCommand command);
|
||||
|
||||
void inject(BackfillRegistrarBillingAccountsCommand command);
|
||||
|
||||
void inject(CheckDomainClaimsCommand command);
|
||||
|
||||
void inject(CheckDomainCommand command);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaJpaTm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.persistence.transaction.Transaction;
|
||||
import google.registry.persistence.transaction.TransactionEntity;
|
||||
import java.util.List;
|
||||
|
||||
@Parameters(separators = " =", commandDescription = "Replay a range of transactions.")
|
||||
public class ReplayTxnsCommand implements CommandWithRemoteApi {
|
||||
|
||||
private static final int BATCH_SIZE = 200;
|
||||
|
||||
@Parameter(
|
||||
names = {"-s", "--start-txn-id"},
|
||||
description = "Transaction id to start replaying at.",
|
||||
required = true)
|
||||
long startTxnId;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
List<TransactionEntity> txns;
|
||||
do {
|
||||
txns =
|
||||
replicaJpaTm()
|
||||
.transact(
|
||||
() ->
|
||||
replicaJpaTm()
|
||||
.query(
|
||||
"SELECT txn FROM TransactionEntity txn where id >= :startTxn ORDER"
|
||||
+ " BY id",
|
||||
TransactionEntity.class)
|
||||
.setParameter("startTxn", startTxnId)
|
||||
.setMaxResults(BATCH_SIZE)
|
||||
.getResultList());
|
||||
for (TransactionEntity txn : txns) {
|
||||
System.out.println("Replaying transaction " + txn.getId());
|
||||
Transaction.deserialize(txn.getContents());
|
||||
startTxnId = txn.getId() + 1;
|
||||
}
|
||||
} while (txns.size() > 0);
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.HostResource;
|
||||
import google.registry.tools.soy.DomainRenewSoyInfo;
|
||||
import google.registry.tools.soy.UniformRapidSuspensionSoyInfo;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -122,12 +123,14 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
protected void initMutatingEppToolCommand() {
|
||||
superuser = true;
|
||||
DateTime now = DateTime.now(UTC);
|
||||
ImmutableSet<String> newHostsSet = ImmutableSet.copyOf(newHosts);
|
||||
ImmutableList<String> newCanonicalHosts =
|
||||
newHosts.stream().map(DomainNameUtils::canonicalizeHostname).collect(toImmutableList());
|
||||
ImmutableSet<String> newHostsSet = ImmutableSet.copyOf(newCanonicalHosts);
|
||||
Optional<DomainBase> domainOpt = loadByForeignKey(DomainBase.class, domainName, now);
|
||||
checkArgumentPresent(domainOpt, "Domain '%s' does not exist or is deleted", domainName);
|
||||
DomainBase domain = domainOpt.get();
|
||||
Set<String> missingHosts =
|
||||
difference(newHostsSet, checkResourcesExist(HostResource.class, newHosts, now));
|
||||
difference(newHostsSet, checkResourcesExist(HostResource.class, newCanonicalHosts, now));
|
||||
checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts);
|
||||
checkArgument(
|
||||
locksToPreserve.isEmpty() || undo,
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -27,6 +29,7 @@ public class UnlockDomainCommand extends LockOrUnlockDomainCommand {
|
||||
|
||||
@Override
|
||||
protected void createAndApplyRequest(String domain) {
|
||||
domainLockUtils.administrativelyApplyUnlock(domain, clientId, true, Optional.empty());
|
||||
domainLockUtils.administrativelyApplyUnlock(
|
||||
canonicalizeHostname(domain), clientId, true, Optional.empty());
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.tools.javascrap;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.model.OteAccountBuilder.DEFAULT_BILLING_ACCOUNT_MAP;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.tools.CommandWithRemoteApi;
|
||||
|
||||
/**
|
||||
* Backfills the billing account maps on all Registrars that don't have any set.
|
||||
*
|
||||
* <p>This should not (and cannot) be used on production. Its purpose is to backfill these values on
|
||||
* sandbox, where most registrars have an empty billing account map, including all that were created
|
||||
* for OT&E. The same default billing account map is used for all registrars, and includes
|
||||
* values for USD and JPY. The actual values here don't matter as we don't do invoicing on sandbox
|
||||
* anyway.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Backfill registrar billing account maps.")
|
||||
public class BackfillRegistrarBillingAccountsCommand implements CommandWithRemoteApi {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
checkState(
|
||||
RegistryEnvironment.get() != RegistryEnvironment.PRODUCTION,
|
||||
"Do not run this on production");
|
||||
System.out.println("Populating billing account maps on all registrars missing them ...");
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().loadAllOfStream(Registrar.class)
|
||||
.filter(r -> r.getBillingAccountMap().isEmpty())
|
||||
.forEach(
|
||||
r ->
|
||||
tm().update(
|
||||
r.asBuilder()
|
||||
.setBillingAccountMap(DEFAULT_BILLING_ACCOUNT_MAP)
|
||||
.build())));
|
||||
System.out.println("Done!");
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools.params;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
|
||||
/** InternetDomainName CLI parameter converter/validator. */
|
||||
public final class InternetDomainNameParameter
|
||||
@@ -26,6 +27,6 @@ public final class InternetDomainNameParameter
|
||||
|
||||
@Override
|
||||
public InternetDomainName convert(String value) {
|
||||
return InternetDomainName.from(value);
|
||||
return InternetDomainName.from(DomainNameUtils.canonicalizeHostname(value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.ui.server;
|
||||
import static com.google.common.collect.Range.atLeast;
|
||||
import static com.google.common.collect.Range.atMost;
|
||||
import static com.google.common.collect.Range.closed;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Splitter;
|
||||
@@ -334,7 +334,7 @@ public final class RegistrarFormFields {
|
||||
if (!InternetDomainName.isValid(input)) {
|
||||
throw new FormFieldException("Not a valid hostname.");
|
||||
}
|
||||
return canonicalizeDomainName(input);
|
||||
return canonicalizeHostname(input);
|
||||
}
|
||||
|
||||
public static @Nullable DateTime parseDateTime(@Nullable String input) {
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.whois;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.tld.Registries.findTldForName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
@@ -122,7 +122,7 @@ class WhoisReader {
|
||||
logger.atInfo().log(
|
||||
"Attempting domain lookup command using domain name '%s'.", tokens.get(1));
|
||||
return commandFactory.domainLookup(
|
||||
InternetDomainName.from(canonicalizeDomainName(tokens.get(1))),
|
||||
InternetDomainName.from(canonicalizeHostname(tokens.get(1))),
|
||||
fullOutput,
|
||||
whoisRedactedEmailText);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
@@ -152,8 +152,8 @@ class WhoisReader {
|
||||
try {
|
||||
logger.atInfo().log(
|
||||
"Attempting nameserver lookup command using %s as a hostname.", tokens.get(1));
|
||||
return commandFactory.nameserverLookupByHost(InternetDomainName.from(
|
||||
canonicalizeDomainName(tokens.get(1))));
|
||||
return commandFactory.nameserverLookupByHost(
|
||||
InternetDomainName.from(canonicalizeHostname(tokens.get(1))));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// Silently ignore this exception.
|
||||
}
|
||||
@@ -187,7 +187,7 @@ class WhoisReader {
|
||||
|
||||
// Try to parse it as a domain name or host name.
|
||||
try {
|
||||
final InternetDomainName targetName = InternetDomainName.from(canonicalizeDomainName(arg1));
|
||||
final InternetDomainName targetName = InternetDomainName.from(canonicalizeHostname(arg1));
|
||||
|
||||
// We don't know at this point whether we have a domain name or a host name. We have to
|
||||
// search through our configured TLDs to see if there's one that prefixes the name.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user