mirror of
https://github.com/google/nomulus
synced 2026-06-09 16:33:02 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6cbc2fa5ef | |||
| 6883093735 | |||
| a6078bc4f4 | |||
| 6b75cf8496 | |||
| 219e9d3afb | |||
| acdbc65c51 | |||
| d510531f65 | |||
| 0d4dd57fe7 | |||
| 2667a0e977 | |||
| 1aef31efff | |||
| 4d19245c29 | |||
| 4b34307a6e | |||
| 55243e7cf6 | |||
| e14764b4c8 | |||
| 68810f7a30 |
@@ -103,7 +103,7 @@ explodeWar.doLast {
|
||||
file("${it.explodedAppDirectory}/WEB-INF/lib/tools.jar").setWritable(true)
|
||||
}
|
||||
|
||||
appengineDeployAll.finalizedBy ':cloudSchedulerDeployer'
|
||||
appengineDeployAll.finalizedBy ':deployCloudSchedulerAndQueue'
|
||||
rootProject.deploy.dependsOn appengineDeployAll
|
||||
rootProject.stage.dependsOn appengineStage
|
||||
tasks['war'].dependsOn ':console-webapp:buildConsoleWebappProd'
|
||||
|
||||
+9
-3
@@ -558,17 +558,23 @@ task coreDev {
|
||||
|
||||
javadocDependentTasks.each { tasks.javadoc.dependsOn(it) }
|
||||
|
||||
// Runs the script, which deploys cloud scheduler tasks based on the config
|
||||
task cloudSchedulerDeployer {
|
||||
// Runs the script, which deploys cloud scheduler and tasks based on the config
|
||||
task deployCloudSchedulerAndQueue {
|
||||
doLast {
|
||||
def env = environment
|
||||
if (!prodOrSandboxEnv) {
|
||||
exec {
|
||||
commandLine 'go', 'run',
|
||||
"${rootDir}/release/builder/cloudSchedulerDeployer.go",
|
||||
"${rootDir}/release/builder/deployCloudSchedulerAndQueue.go",
|
||||
"${rootDir}/core/src/main/java/google/registry/env/${env}/default/WEB-INF/cloud-scheduler-tasks.xml",
|
||||
"domain-registry-${env}"
|
||||
}
|
||||
exec {
|
||||
commandLine 'go', 'run',
|
||||
"${rootDir}/release/builder/deployCloudSchedulerAndQueue.go",
|
||||
"${rootDir}/core/src/main/java/google/registry/env/common/default/WEB-INF/cloud-tasks-queue.xml",
|
||||
"domain-registry-${env}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+60
-48
@@ -3898,10 +3898,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/cors": {
|
||||
"version": "2.8.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
|
||||
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
|
||||
"dev": true
|
||||
"version": "2.8.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
|
||||
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.4.6",
|
||||
@@ -5907,9 +5910,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
|
||||
"integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
|
||||
"integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
@@ -5921,16 +5924,16 @@
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.3",
|
||||
"ws": "~8.2.3"
|
||||
"ws": "~8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
|
||||
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
|
||||
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -10880,27 +10883,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz",
|
||||
"integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==",
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz",
|
||||
"integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io": "~6.2.0",
|
||||
"socket.io-adapter": "~2.4.0",
|
||||
"socket.io-parser": "~4.2.0"
|
||||
"engine.io": "~6.4.1",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
|
||||
"integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==",
|
||||
"dev": true
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
|
||||
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ws": "~8.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.1",
|
||||
@@ -12219,9 +12225,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
|
||||
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -15175,10 +15181,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/cors": {
|
||||
"version": "2.8.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
|
||||
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
|
||||
"dev": true
|
||||
"version": "2.8.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz",
|
||||
"integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/eslint": {
|
||||
"version": "8.4.6",
|
||||
@@ -16746,9 +16755,9 @@
|
||||
}
|
||||
},
|
||||
"engine.io": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
|
||||
"integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
|
||||
"version": "6.4.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz",
|
||||
"integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
@@ -16760,13 +16769,13 @@
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.0.3",
|
||||
"ws": "~8.2.3"
|
||||
"ws": "~8.11.0"
|
||||
}
|
||||
},
|
||||
"engine.io-parser": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
|
||||
"integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
|
||||
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
|
||||
"dev": true
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
@@ -20509,24 +20518,27 @@
|
||||
"dev": true
|
||||
},
|
||||
"socket.io": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.2.tgz",
|
||||
"integrity": "sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==",
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz",
|
||||
"integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io": "~6.2.0",
|
||||
"socket.io-adapter": "~2.4.0",
|
||||
"socket.io-parser": "~4.2.0"
|
||||
"engine.io": "~6.4.1",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.1"
|
||||
}
|
||||
},
|
||||
"socket.io-adapter": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
|
||||
"integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==",
|
||||
"dev": true
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
|
||||
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ws": "~8.11.0"
|
||||
}
|
||||
},
|
||||
"socket.io-parser": {
|
||||
"version": "4.2.1",
|
||||
@@ -21486,9 +21498,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
|
||||
"integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.batch;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.cannedscript.CannedScripts;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.Auth;
|
||||
import javax.inject.Inject;
|
||||
@@ -25,15 +24,15 @@ import javax.inject.Inject;
|
||||
/**
|
||||
* Action that executes a canned script specified by the caller.
|
||||
*
|
||||
* <p>This class is introduced to help the safe rollout of credential changes. The delegated
|
||||
* credentials in particular, benefit from this: they require manual configuration of the peer
|
||||
* system in each environment, and may wait hours or even days after deployment until triggered by
|
||||
* user activities.
|
||||
* <p>This class provides a hook for invoking hard-coded methods. The main use case is to verify in
|
||||
* Sandbox and Production environments new features that depend on environment-specific
|
||||
* configurations. For example, the {@code DelegatedCredential}, which requires correct GWorkspace
|
||||
* configuration, has been tested this way. Since it is a hassle to add or remove endpoints, we keep
|
||||
* this class all the time.
|
||||
*
|
||||
* <p>This action can be invoked using the Nomulus CLI command: {@code nomulus -e ${env} curl
|
||||
* --service BACKEND -X POST -u '/_dr/task/executeCannedScript?script=${script_name}'}
|
||||
* --service BACKEND -X POST -u '/_dr/task/executeCannedScript}'}
|
||||
*/
|
||||
// TODO(b/277239043): remove class after credential changes are rolled out.
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = "/_dr/task/executeCannedScript",
|
||||
@@ -51,7 +50,7 @@ public class CannedScriptExecutionAction implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
CannedScripts.runAllChecks();
|
||||
// Invoke canned scripts here.
|
||||
logger.atInfo().log("Finished running scripts.");
|
||||
} catch (Throwable t) {
|
||||
logger.atWarning().withCause(t).log("Error executing scripts.");
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.batch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.tools.ServiceConnection.getServer;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import com.google.api.gax.rpc.ApiException;
|
||||
@@ -23,6 +24,8 @@ import com.google.cloud.tasks.v2.AppEngineHttpRequest;
|
||||
import com.google.cloud.tasks.v2.AppEngineRouting;
|
||||
import com.google.cloud.tasks.v2.CloudTasksClient;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.cloud.tasks.v2.HttpRequest;
|
||||
import com.google.cloud.tasks.v2.OidcToken;
|
||||
import com.google.cloud.tasks.v2.QueueName;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.base.Joiner;
|
||||
@@ -46,7 +49,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@@ -61,6 +67,9 @@ public class CloudTasksUtils implements Serializable {
|
||||
private final Clock clock;
|
||||
private final String projectId;
|
||||
private final String locationId;
|
||||
// defaultServiceAccount and iapClientId are nullable because Optional isn't serializable
|
||||
@Nullable private final String defaultServiceAccount;
|
||||
@Nullable private final String iapClientId;
|
||||
private final SerializableCloudTasksClient client;
|
||||
|
||||
@Inject
|
||||
@@ -69,11 +78,15 @@ public class CloudTasksUtils implements Serializable {
|
||||
Clock clock,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("locationId") String locationId,
|
||||
@Config("defaultServiceAccount") Optional<String> defaultServiceAccount,
|
||||
@Config("iapClientId") Optional<String> iapClientId,
|
||||
SerializableCloudTasksClient client) {
|
||||
this.retrier = retrier;
|
||||
this.clock = clock;
|
||||
this.projectId = projectId;
|
||||
this.locationId = locationId;
|
||||
this.defaultServiceAccount = defaultServiceAccount.orElse(null);
|
||||
this.iapClientId = iapClientId.orElse(null);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@@ -98,6 +111,74 @@ public class CloudTasksUtils implements Serializable {
|
||||
return enqueue(queue, Arrays.asList(tasks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a (possible) set of params into an HTTP request via the appropriate method.
|
||||
*
|
||||
* <p>For GET requests we add them on to the URL, and for POST requests we add them in the body of
|
||||
* the request.
|
||||
*
|
||||
* <p>The parameters {@code putHeadersFunction} and {@code setBodyFunction} are used so that this
|
||||
* method can be called with either an AppEngine HTTP request or a standard non-AppEngine HTTP
|
||||
* request. The two objects do not have the same methods, but both have ways of setting headers /
|
||||
* body.
|
||||
*
|
||||
* @return the resulting path (unchanged for POST requests, with params added for GET requests)
|
||||
*/
|
||||
private String processRequestParameters(
|
||||
String path,
|
||||
HttpMethod method,
|
||||
Multimap<String, String> params,
|
||||
BiConsumer<String, String> putHeadersFunction,
|
||||
Consumer<ByteString> setBodyFunction) {
|
||||
if (CollectionUtils.isNullOrEmpty(params)) {
|
||||
return path;
|
||||
}
|
||||
Escaper escaper = UrlEscapers.urlPathSegmentEscaper();
|
||||
String encodedParams =
|
||||
Joiner.on("&")
|
||||
.join(
|
||||
params.entries().stream()
|
||||
.map(
|
||||
entry ->
|
||||
String.format(
|
||||
"%s=%s",
|
||||
escaper.escape(entry.getKey()), escaper.escape(entry.getValue())))
|
||||
.collect(toImmutableList()));
|
||||
if (method.equals(HttpMethod.GET)) {
|
||||
return String.format("%s?%s", path, encodedParams);
|
||||
}
|
||||
putHeadersFunction.accept(HttpHeaders.CONTENT_TYPE, MediaType.FORM_DATA.toString());
|
||||
setBodyFunction.accept(ByteString.copyFrom(encodedParams, StandardCharsets.UTF_8));
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Task} that does not use AppEngine for submission.
|
||||
*
|
||||
* <p>This uses the standard Cloud Tasks auth format to create and send an OIDC ID token set to
|
||||
* the default service account. That account must have permission to submit tasks to Cloud Tasks.
|
||||
*/
|
||||
private Task createNonAppEngineTask(
|
||||
String path, HttpMethod method, Service service, Multimap<String, String> params) {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder().setHttpMethod(method);
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
OidcToken.Builder oidcTokenBuilder =
|
||||
OidcToken.newBuilder().setServiceAccountEmail(defaultServiceAccount);
|
||||
// If the service is using IAP, add that as the audience for the token so the request can be
|
||||
// appropriately authed. Otherwise, use the project name.
|
||||
if (iapClientId != null) {
|
||||
oidcTokenBuilder.setAudience(iapClientId);
|
||||
} else {
|
||||
oidcTokenBuilder.setAudience(projectId);
|
||||
}
|
||||
requestBuilder.setOidcToken(oidcTokenBuilder.build());
|
||||
String totalPath = String.format("%s%s", getServer(service), path);
|
||||
requestBuilder.setUrl(totalPath);
|
||||
return Task.newBuilder().setHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Task} to be enqueued.
|
||||
*
|
||||
@@ -123,34 +204,21 @@ public class CloudTasksUtils implements Serializable {
|
||||
method.equals(HttpMethod.GET) || method.equals(HttpMethod.POST),
|
||||
"HTTP method %s is used. Only GET and POST are allowed.",
|
||||
method);
|
||||
AppEngineHttpRequest.Builder requestBuilder =
|
||||
AppEngineHttpRequest.newBuilder()
|
||||
.setHttpMethod(method)
|
||||
.setAppEngineRouting(
|
||||
AppEngineRouting.newBuilder().setService(service.toString()).build());
|
||||
|
||||
if (!CollectionUtils.isNullOrEmpty(params)) {
|
||||
Escaper escaper = UrlEscapers.urlPathSegmentEscaper();
|
||||
String encodedParams =
|
||||
Joiner.on("&")
|
||||
.join(
|
||||
params.entries().stream()
|
||||
.map(
|
||||
entry ->
|
||||
String.format(
|
||||
"%s=%s",
|
||||
escaper.escape(entry.getKey()), escaper.escape(entry.getValue())))
|
||||
.collect(toImmutableList()));
|
||||
if (method == HttpMethod.GET) {
|
||||
path = String.format("%s?%s", path, encodedParams);
|
||||
} else {
|
||||
requestBuilder
|
||||
.putHeaders(HttpHeaders.CONTENT_TYPE, MediaType.FORM_DATA.toString())
|
||||
.setBody(ByteString.copyFrom(encodedParams, StandardCharsets.UTF_8));
|
||||
}
|
||||
// If the default service account is configured, send a standard non-AppEngine HTTP request
|
||||
if (defaultServiceAccount != null) {
|
||||
return createNonAppEngineTask(path, method, service, params);
|
||||
} else {
|
||||
AppEngineHttpRequest.Builder requestBuilder =
|
||||
AppEngineHttpRequest.newBuilder()
|
||||
.setHttpMethod(method)
|
||||
.setAppEngineRouting(
|
||||
AppEngineRouting.newBuilder().setService(service.toString()).build());
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
requestBuilder.setRelativeUri(path);
|
||||
return Task.newBuilder().setAppEngineHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
requestBuilder.setRelativeUri(path);
|
||||
return Task.newBuilder().setAppEngineHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.batch.BatchModule.PARAM_DRY_RUN;
|
||||
import static google.registry.config.RegistryEnvironment.PRODUCTION;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_DELETE;
|
||||
import static google.registry.model.tld.Registries.getTldsOfType;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -32,7 +33,6 @@ import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -98,8 +98,6 @@ public class DeleteProberDataAction implements Runnable {
|
||||
/** Number of domains to retrieve and delete per SQL transaction. */
|
||||
private static final int BATCH_SIZE = 1000;
|
||||
|
||||
@Inject DnsUtils dnsUtils;
|
||||
|
||||
@Inject
|
||||
@Parameter(PARAM_DRY_RUN)
|
||||
boolean isDryRun;
|
||||
@@ -222,7 +220,7 @@ public class DeleteProberDataAction implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void hardDeleteDomainsAndHosts(
|
||||
private static void hardDeleteDomainsAndHosts(
|
||||
ImmutableList<String> domainRepoIds, ImmutableList<String> hostNames) {
|
||||
tm().query("DELETE FROM Host WHERE hostName IN :hostNames")
|
||||
.setParameter("hostNames", hostNames)
|
||||
@@ -264,6 +262,6 @@ public class DeleteProberDataAction implements Runnable {
|
||||
// messages, or auto-renews because those will all be hard-deleted the next time the job runs
|
||||
// anyway.
|
||||
tm().putAll(ImmutableList.of(deletedDomain, historyEntry));
|
||||
dnsUtils.requestDomainDnsRefresh(deletedDomain.getDomainName());
|
||||
requestDomainDnsRefresh(deletedDomain.getDomainName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch.cannedscript;
|
||||
|
||||
import com.google.api.gax.core.FixedCredentialsProvider;
|
||||
import com.google.api.services.bigquery.Bigquery;
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import com.google.api.services.dns.Dns;
|
||||
import com.google.cloud.storage.Storage;
|
||||
import com.google.cloud.storage.StorageOptions;
|
||||
import com.google.cloud.tasks.v2.CloudTasksClient;
|
||||
import com.google.cloud.tasks.v2.CloudTasksSettings;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import dagger.Component;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import google.registry.util.UtilsModule;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/** Canned actions invoked from {@link google.registry.batch.CannedScriptExecutionAction}. */
|
||||
// TODO(b/277239043): remove class after credential changes are rolled out.
|
||||
public class CannedScripts {
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final Supplier<CannedScriptsComponent> COMPONENT_SUPPLIER =
|
||||
Suppliers.memoize(DaggerCannedScripts_CannedScriptsComponent::create);
|
||||
|
||||
public static void runAllChecks() {
|
||||
CannedScriptsComponent component = COMPONENT_SUPPLIER.get();
|
||||
String projectId = component.projectId();
|
||||
Bigquery bigquery = component.bigQuery();
|
||||
try {
|
||||
bigquery.datasets().list(projectId).execute().getDatasets().stream()
|
||||
.findAny()
|
||||
.ifPresent(
|
||||
datasets ->
|
||||
logger.atInfo().log("Found a BQ dataset [%s]", datasets.getFriendlyName()));
|
||||
logger.atInfo().log("Finished accessing BQ.");
|
||||
} catch (IOException ioe) {
|
||||
logger.atSevere().withCause(ioe).log("Failed to access bigquery.");
|
||||
}
|
||||
try {
|
||||
Dataflow dataflow = component.dataflow();
|
||||
dataflow.projects().jobs().list(projectId).execute().getJobs().stream()
|
||||
.findAny()
|
||||
.ifPresent(job -> logger.atInfo().log("Found a job [%s]", job.getName()));
|
||||
logger.atInfo().log("Finished accessing Dataflow.");
|
||||
} catch (IOException ioe) {
|
||||
logger.atSevere().withCause(ioe).log("Failed to access dataflow.");
|
||||
}
|
||||
try {
|
||||
Storage gcs = component.gcs();
|
||||
gcs.listAcls(projectId + "-beam");
|
||||
logger.atInfo().log("Finished accessing gcs.");
|
||||
} catch (RuntimeException e) {
|
||||
logger.atSevere().withCause(e).log("Failed to access gcs.");
|
||||
}
|
||||
try {
|
||||
Dns dns = component.dns();
|
||||
dns.managedZones().list(projectId).execute().getManagedZones().stream()
|
||||
.findAny()
|
||||
.ifPresent(zone -> logger.atInfo().log("Found one zone [%s].", zone.getName()));
|
||||
logger.atInfo().log("Finished accessing dns.");
|
||||
} catch (IOException ioe) {
|
||||
logger.atSevere().withCause(ioe).log("Failed to access dns.");
|
||||
}
|
||||
try {
|
||||
CloudTasksClient client = component.cloudtasksClient();
|
||||
com.google.cloud.tasks.v2.Queue queue =
|
||||
client.getQueue(
|
||||
String.format(
|
||||
"projects/%s/locations/%s/queues/async-actions",
|
||||
projectId, component.locationId()));
|
||||
logger.atInfo().log("Got async queue state [%s]", queue.getState().name());
|
||||
logger.atInfo().log("Finished accessing cloudtasks.");
|
||||
} catch (RuntimeException e) {
|
||||
logger.atSevere().withCause(e).log("Failed to access cloudtasks.");
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Component(
|
||||
modules = {
|
||||
ConfigModule.class,
|
||||
CredentialModule.class,
|
||||
CannedScriptsModule.class,
|
||||
UtilsModule.class
|
||||
})
|
||||
interface CannedScriptsComponent {
|
||||
Bigquery bigQuery();
|
||||
|
||||
CloudTasksClient cloudtasksClient();
|
||||
|
||||
Dataflow dataflow();
|
||||
|
||||
Dns dns();
|
||||
|
||||
Storage gcs();
|
||||
|
||||
@Config("projectId")
|
||||
String projectId();
|
||||
|
||||
@Config("locationId")
|
||||
String locationId();
|
||||
}
|
||||
|
||||
@Module
|
||||
static class CannedScriptsModule {
|
||||
@Provides
|
||||
static Bigquery provideBigquery(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId) {
|
||||
return new Bigquery.Builder(
|
||||
credentialsBundle.getHttpTransport(),
|
||||
credentialsBundle.getJsonFactory(),
|
||||
credentialsBundle.getHttpRequestInitializer())
|
||||
.setApplicationName(projectId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
static Dataflow provideDataflow(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId) {
|
||||
return new Dataflow.Builder(
|
||||
credentialsBundle.getHttpTransport(),
|
||||
credentialsBundle.getJsonFactory(),
|
||||
credentialsBundle.getHttpRequestInitializer())
|
||||
.setApplicationName(String.format("%s billing", projectId))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
static Storage provideGcs(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle) {
|
||||
return StorageOptions.newBuilder()
|
||||
.setCredentials(credentialsBundle.getGoogleCredentials())
|
||||
.build()
|
||||
.getService();
|
||||
}
|
||||
|
||||
@Provides
|
||||
static Dns provideDns(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("cloudDnsRootUrl") Optional<String> rootUrl,
|
||||
@Config("cloudDnsServicePath") Optional<String> servicePath) {
|
||||
Dns.Builder builder =
|
||||
new Dns.Builder(
|
||||
credentialsBundle.getHttpTransport(),
|
||||
credentialsBundle.getJsonFactory(),
|
||||
credentialsBundle.getHttpRequestInitializer())
|
||||
.setApplicationName(projectId);
|
||||
|
||||
rootUrl.ifPresent(builder::setRootUrl);
|
||||
servicePath.ifPresent(builder::setServicePath);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
public static CloudTasksClient provideCloudTasksClient(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentials) {
|
||||
CloudTasksClient client;
|
||||
try {
|
||||
client =
|
||||
CloudTasksClient.create(
|
||||
CloudTasksSettings.newBuilder()
|
||||
.setCredentialsProvider(
|
||||
FixedCredentialsProvider.create(credentials.getGoogleCredentials()))
|
||||
.build());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,7 +189,7 @@ public abstract class BillingEvent implements Serializable {
|
||||
.minusDays(1)
|
||||
.toString(),
|
||||
billingId(),
|
||||
String.format("%s - %s", registrarId(), tld()),
|
||||
registrarId(),
|
||||
String.format("%s | TLD: %s | TERM: %d-year", action(), tld(), years()),
|
||||
amount(),
|
||||
currency(),
|
||||
@@ -233,7 +233,7 @@ public abstract class BillingEvent implements Serializable {
|
||||
/** Returns the billing account id, which is the {@code BillingEvent.billingId}. */
|
||||
abstract String productAccountKey();
|
||||
|
||||
/** Returns the invoice grouping key, which is in the format "registrarId - tld". */
|
||||
/** Returns the invoice grouping key, which is the registrar ID. */
|
||||
abstract String usageGroupingKey();
|
||||
|
||||
/** Returns a description of the item, formatted as "action | TLD: tld | TERM: n-year." */
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.Multibinds;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.util.Map;
|
||||
@@ -34,7 +34,7 @@ public abstract class BigqueryModule {
|
||||
|
||||
@Provides
|
||||
static Bigquery provideBigquery(
|
||||
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId) {
|
||||
return new Bigquery.Builder(
|
||||
credentialsBundle.getHttpTransport(),
|
||||
|
||||
@@ -22,7 +22,7 @@ import dagger.Provides;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.batch.CloudTasksUtils.GcpCloudTasksClient;
|
||||
import google.registry.batch.CloudTasksUtils.SerializableCloudTasksClient;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.io.IOException;
|
||||
@@ -41,7 +41,7 @@ public abstract class CloudTasksUtilsModule {
|
||||
// Provides a supplier instead of using a Dagger @Provider because the latter is not serializable.
|
||||
@Provides
|
||||
public static Supplier<CloudTasksClient> provideCloudTasksClientSupplier(
|
||||
@DefaultCredential GoogleCredentialsBundle credentials) {
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentials) {
|
||||
return (Supplier<CloudTasksClient> & Serializable)
|
||||
() -> {
|
||||
CloudTasksClient client;
|
||||
|
||||
@@ -66,38 +66,6 @@ public abstract class CredentialModule {
|
||||
return GoogleCredentialsBundle.create(credential);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the default {@link GoogleCredentialsBundle} from the Google Cloud runtime.
|
||||
*
|
||||
* <p>The credential returned depends on the runtime environment:
|
||||
*
|
||||
* <ul>
|
||||
* <li>On AppEngine, returns the service account credential for
|
||||
* PROJECT_ID@appspot.gserviceaccount.com
|
||||
* <li>On Compute Engine, returns the service account credential for
|
||||
* PROJECT_NUMBER-compute@developer.gserviceaccount.com
|
||||
* <li>On end user host, this returns the credential downloaded by gcloud. Please refer to <a
|
||||
* href="https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login">Cloud
|
||||
* SDK documentation</a> for details.
|
||||
* </ul>
|
||||
*/
|
||||
@DefaultCredential
|
||||
@Provides
|
||||
@Singleton
|
||||
public static GoogleCredentialsBundle provideDefaultCredential(
|
||||
@Config("defaultCredentialOauthScopes") ImmutableList<String> requiredScopes) {
|
||||
GoogleCredentials credential;
|
||||
try {
|
||||
credential = GoogleCredentials.getApplicationDefault();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (credential.createScopedRequired()) {
|
||||
credential = credential.createScoped(requiredScopes);
|
||||
}
|
||||
return GoogleCredentialsBundle.create(credential);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link GoogleCredentialsBundle} for accessing Google Workspace APIs, such as Drive
|
||||
* and Sheets.
|
||||
@@ -162,13 +130,6 @@ public abstract class CredentialModule {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ApplicationDefaultCredential {}
|
||||
|
||||
/** Dagger qualifier for the Application Default Credential. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Deprecated // Switching to @ApplicationDefaultCredential
|
||||
public @interface DefaultCredential {}
|
||||
|
||||
/** Dagger qualifier for the credential for Google Workspace APIs. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
|
||||
@@ -108,12 +108,6 @@ public final class RegistryConfig {
|
||||
return config.gcpProject.projectId;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("serviceAccountEmails")
|
||||
public static ImmutableList<String> provideServiceAccountEmails(RegistryConfigSettings config) {
|
||||
return ImmutableList.copyOf(config.gcpProject.serviceAccountEmails);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("projectIdNumber")
|
||||
public static long provideProjectIdNumber(RegistryConfigSettings config) {
|
||||
@@ -126,6 +120,18 @@ public final class RegistryConfig {
|
||||
return config.gcpProject.locationId;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("serviceAccountEmails")
|
||||
public static ImmutableList<String> provideServiceAccountEmails(RegistryConfigSettings config) {
|
||||
return ImmutableList.copyOf(config.gcpProject.serviceAccountEmails);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("defaultServiceAccount")
|
||||
public static Optional<String> provideDefaultServiceAccount(RegistryConfigSettings config) {
|
||||
return Optional.ofNullable(config.gcpProject.defaultServiceAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* The filename of the logo to be displayed in the header of the registrar console.
|
||||
*
|
||||
|
||||
@@ -55,6 +55,7 @@ public class RegistryConfigSettings {
|
||||
public String toolsServiceUrl;
|
||||
public String pubapiServiceUrl;
|
||||
public List<String> serviceAccountEmails;
|
||||
public String defaultServiceAccount;
|
||||
}
|
||||
|
||||
/** Configuration options for OAuth settings for authenticating users. */
|
||||
|
||||
@@ -27,6 +27,9 @@ gcpProject:
|
||||
serviceAccountEmails:
|
||||
- default-service-account-email@email.com
|
||||
- cloud-scheduler-email@email.com
|
||||
# The default service account with which the service is running. For example,
|
||||
# on GAE this would be {project-id}@appspot.gserviceaccount.com
|
||||
defaultServiceAccount: null
|
||||
|
||||
gSuite:
|
||||
# Publicly accessible domain name of the running G Suite instance.
|
||||
|
||||
@@ -140,13 +140,25 @@ public final class TldFanoutAction implements Runnable {
|
||||
for (String tld : tlds) {
|
||||
Task task = createTask(tld, flowThruParams);
|
||||
Task createdTask = cloudTasksUtils.enqueue(queue, task);
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(), tld, createdTask.getAppEngineHttpRequest().getRelativeUri()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getAppEngineHttpRequest().getRelativeUri());
|
||||
if (createdTask.hasAppEngineHttpRequest()) {
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(),
|
||||
tld,
|
||||
createdTask.getAppEngineHttpRequest().getRelativeUri()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getAppEngineHttpRequest().getRelativeUri());
|
||||
} else {
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl());
|
||||
}
|
||||
}
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
response.setPayload(outputPayload.toString());
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
/** Static class for DNS-related constants. */
|
||||
public class DnsConstants {
|
||||
private DnsConstants() {}
|
||||
|
||||
/** The name of the DNS pull queue. */
|
||||
public static final String DNS_PULL_QUEUE_NAME = "dns-pull"; // See queue.xml.
|
||||
|
||||
/** The name of the DNS publish push queue. */
|
||||
public static final String DNS_PUBLISH_PUSH_QUEUE_NAME = "dns-publish"; // See queue.xml.
|
||||
|
||||
/** The parameter to use for storing the target type ("domain" or "host" or "zone"). */
|
||||
public static final String DNS_TARGET_TYPE_PARAM = "Target-Type";
|
||||
|
||||
/** The parameter to use for storing the target name (domain or host name) with the task. */
|
||||
public static final String DNS_TARGET_NAME_PARAM = "Target-Name";
|
||||
|
||||
/** The parameter to use for storing the creation time with the task. */
|
||||
public static final String DNS_TARGET_CREATE_TIME_PARAM = "Create-Time";
|
||||
|
||||
/** The possible values of the {@code DNS_TARGET_TYPE_PARAM} parameter. */
|
||||
public enum TargetType {
|
||||
DOMAIN,
|
||||
HOST
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsConstants.DNS_PULL_QUEUE_NAME;
|
||||
import static google.registry.dns.RefreshDnsOnHostRenameAction.PARAM_HOST_KEY;
|
||||
import static google.registry.request.RequestParameters.extractEnumParameter;
|
||||
import static google.registry.request.RequestParameters.extractIntParameter;
|
||||
@@ -24,20 +22,17 @@ import static google.registry.request.RequestParameters.extractOptionalParameter
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
import static google.registry.request.RequestParameters.extractSetOfParameters;
|
||||
|
||||
import com.google.appengine.api.taskqueue.Queue;
|
||||
import com.google.appengine.api.taskqueue.QueueFactory;
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.dns.writer.DnsWriterZone;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestParameters;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.inject.Named;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@@ -71,18 +66,6 @@ public abstract class DnsModule {
|
||||
return Hashing.murmur3_32_fixed();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(DNS_PULL_QUEUE_NAME)
|
||||
static Queue provideDnsPullQueue() {
|
||||
return QueueFactory.getQueue(DNS_PULL_QUEUE_NAME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(DNS_PUBLISH_PUSH_QUEUE_NAME)
|
||||
static Queue provideDnsUpdatePushQueue() {
|
||||
return QueueFactory.getQueue(DNS_PUBLISH_PUSH_QUEUE_NAME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(PARAM_PUBLISH_TASK_ENQUEUED)
|
||||
static DateTime provideCreateTime(HttpServletRequest req) {
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.dns.DnsConstants.DNS_PULL_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_CREATE_TIME_PARAM;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
|
||||
import static google.registry.model.tld.Registries.assertTldExists;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import com.google.appengine.api.taskqueue.Queue;
|
||||
import com.google.appengine.api.taskqueue.QueueConstants;
|
||||
import com.google.appengine.api.taskqueue.TaskHandle;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions.Method;
|
||||
import com.google.appengine.api.taskqueue.TransientFailureException;
|
||||
import com.google.apphosting.api.DeadlineExceededException;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Methods for manipulating the queue used for DNS write tasks.
|
||||
*
|
||||
* <p>This includes a {@link RateLimiter} to limit the {@link Queue#leaseTasks} call rate to 9 QPS,
|
||||
* to stay under the 10 QPS limit for this function.
|
||||
*
|
||||
* <p>Note that overlapping calls to {@link ReadDnsQueueAction} (the only place where {@link
|
||||
* DnsQueue#leaseTasks} is used) will have different rate limiters, so they could exceed the allowed
|
||||
* rate. This should be rare though - because {@link DnsQueue#leaseTasks} is only used in {@link
|
||||
* ReadDnsQueueAction}, which is run as a cron job with running time shorter than the cron repeat
|
||||
* time - meaning there should never be two instances running at once.
|
||||
*
|
||||
* @see RegistryConfig.ConfigModule#provideReadDnsRefreshRequestsRuntime()
|
||||
*/
|
||||
public class DnsQueue {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final Queue queue;
|
||||
|
||||
final Clock clock;
|
||||
|
||||
// Queue.leaseTasks is limited to 10 requests per second as per
|
||||
// https://cloud.google.com/appengine/docs/standard/java/javadoc/com/google/appengine/api/taskqueue/Queue.html
|
||||
// "If you generate more than 10 LeaseTasks requests per second, only the first 10 requests will
|
||||
// return results. The others will return no results."
|
||||
private static final RateLimiter rateLimiter = RateLimiter.create(9);
|
||||
|
||||
@Inject
|
||||
DnsQueue(@Named(DNS_PULL_QUEUE_NAME) Queue queue, Clock clock) {
|
||||
this.queue = queue;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
Clock getClock() {
|
||||
return clock;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static DnsQueue createForTesting(Clock clock) {
|
||||
return new DnsQueue(getQueue(DNS_PULL_QUEUE_NAME), clock);
|
||||
}
|
||||
|
||||
@NonFinalForTesting
|
||||
@VisibleForTesting
|
||||
long leaseTasksBatchSize = QueueConstants.maxLeaseCount();
|
||||
|
||||
/** Enqueues the given task type with the given target name to the DNS queue. */
|
||||
private TaskHandle addToQueue(
|
||||
TargetType targetType, String targetName, String tld, Duration countdown) {
|
||||
logger.atInfo().log(
|
||||
"Adding task type=%s, target=%s, tld=%s to pull queue %s (%d tasks currently on queue).",
|
||||
targetType, targetName, tld, DNS_PULL_QUEUE_NAME, queue.fetchStatistics().getNumTasks());
|
||||
return queue.add(
|
||||
TaskOptions.Builder.withDefaults()
|
||||
.method(Method.PULL)
|
||||
.countdownMillis(countdown.getMillis())
|
||||
.param(DNS_TARGET_TYPE_PARAM, targetType.toString())
|
||||
.param(DNS_TARGET_NAME_PARAM, targetName)
|
||||
.param(DNS_TARGET_CREATE_TIME_PARAM, clock.nowUtc().toString())
|
||||
.param(PARAM_TLD, tld));
|
||||
}
|
||||
|
||||
/** Adds a task to the queue to refresh the DNS information for the specified subordinate host. */
|
||||
TaskHandle addHostRefreshTask(String hostName) {
|
||||
Optional<InternetDomainName> tld = Registries.findTldForName(InternetDomainName.from(hostName));
|
||||
checkArgument(
|
||||
tld.isPresent(), String.format("%s is not a subordinate host to a known tld", hostName));
|
||||
return addToQueue(TargetType.HOST, hostName, tld.get().toString(), Duration.ZERO);
|
||||
}
|
||||
|
||||
/** Enqueues a task to refresh DNS for the specified domain now. */
|
||||
TaskHandle addDomainRefreshTask(String domainName) {
|
||||
return addDomainRefreshTask(domainName, Duration.ZERO);
|
||||
}
|
||||
|
||||
/** Enqueues a task to refresh DNS for the specified domain at some point in the future. */
|
||||
TaskHandle addDomainRefreshTask(String domainName, Duration countdown) {
|
||||
return addToQueue(
|
||||
TargetType.DOMAIN,
|
||||
domainName,
|
||||
assertTldExists(getTldFromDomainName(domainName)),
|
||||
countdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of tasks that can be leased with {@link #leaseTasks}.
|
||||
*
|
||||
* <p>If this many tasks are returned, then there might be more tasks still waiting in the queue.
|
||||
*
|
||||
* <p>If less than this number of tasks are returned, then there are no more items in the queue.
|
||||
*/
|
||||
public long getLeaseTasksBatchSize() {
|
||||
return leaseTasksBatchSize;
|
||||
}
|
||||
|
||||
/** Returns handles for a batch of tasks, leased for the specified duration. */
|
||||
public List<TaskHandle> leaseTasks(Duration leaseDuration) {
|
||||
try {
|
||||
rateLimiter.acquire();
|
||||
int numTasks = queue.fetchStatistics().getNumTasks();
|
||||
logger.at((numTasks >= leaseTasksBatchSize) ? Level.WARNING : Level.INFO).log(
|
||||
"There are %d tasks in the DNS queue '%s'.", numTasks, DNS_PULL_QUEUE_NAME);
|
||||
return queue.leaseTasks(leaseDuration.getMillis(), MILLISECONDS, leaseTasksBatchSize);
|
||||
} catch (TransientFailureException | DeadlineExceededException e) {
|
||||
logger.atSevere().withCause(e).log("Failed leasing tasks too fast.");
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete a list of tasks, removing them from the queue permanently. */
|
||||
public void deleteTasks(List<TaskHandle> tasks) {
|
||||
try {
|
||||
queue.deleteTask(tasks);
|
||||
} catch (TransientFailureException | DeadlineExceededException e) {
|
||||
logger.atSevere().withCause(e).log("Failed deleting tasks too fast.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,57 +19,42 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/** Utility class to handle DNS refresh requests. */
|
||||
// TODO: Make this a static util function once we are done with the DNS pull queue migration.
|
||||
public class DnsUtils {
|
||||
public final class DnsUtils {
|
||||
|
||||
private final DnsQueue dnsQueue;
|
||||
/** The name of the DNS publish push queue. */
|
||||
public static final String DNS_PUBLISH_PUSH_QUEUE_NAME = "dns-publish"; // See queue.xml.
|
||||
|
||||
@Inject
|
||||
DnsUtils(DnsQueue dnsQueue) {
|
||||
this.dnsQueue = dnsQueue;
|
||||
}
|
||||
private DnsUtils() {}
|
||||
|
||||
private void requestDnsRefresh(String name, TargetType type, Duration delay) {
|
||||
private static void requestDnsRefresh(String name, TargetType type, Duration delay) {
|
||||
// Throws an IllegalArgumentException if the name is not under a managed TLD -- we only update
|
||||
// DNS for names that are under our management.
|
||||
String tld = Registries.findTldForNameOrThrow(InternetDomainName.from(name)).toString();
|
||||
if (usePullQueue()) {
|
||||
if (TargetType.HOST.equals(type)) {
|
||||
dnsQueue.addHostRefreshTask(name);
|
||||
} else {
|
||||
dnsQueue.addDomainRefreshTask(name, delay);
|
||||
}
|
||||
} else {
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().insert(
|
||||
new DnsRefreshRequest(
|
||||
type, name, tld, tm().getTransactionTime().plus(delay))));
|
||||
}
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().insert(
|
||||
new DnsRefreshRequest(
|
||||
type, name, tld, tm().getTransactionTime().plus(delay))));
|
||||
}
|
||||
|
||||
public void requestDomainDnsRefresh(String domainName, Duration delay) {
|
||||
public static void requestDomainDnsRefresh(String domainName, Duration delay) {
|
||||
requestDnsRefresh(domainName, TargetType.DOMAIN, delay);
|
||||
}
|
||||
|
||||
public void requestDomainDnsRefresh(String domainName) {
|
||||
public static void requestDomainDnsRefresh(String domainName) {
|
||||
requestDomainDnsRefresh(domainName, Duration.ZERO);
|
||||
}
|
||||
|
||||
public void requestHostDnsRefresh(String hostName) {
|
||||
public static void requestHostDnsRefresh(String hostName) {
|
||||
requestDnsRefresh(hostName, TargetType.HOST, Duration.ZERO);
|
||||
}
|
||||
|
||||
@@ -85,7 +70,7 @@ public class DnsUtils {
|
||||
* <li>The last time they were processed is before the cooldown period.
|
||||
* </ul>
|
||||
*/
|
||||
public ImmutableList<DnsRefreshRequest> readAndUpdateRequestsWithLatestProcessTime(
|
||||
public static ImmutableList<DnsRefreshRequest> readAndUpdateRequestsWithLatestProcessTime(
|
||||
String tld, Duration cooldown, int batchSize) {
|
||||
return tm().transact(
|
||||
() -> {
|
||||
@@ -105,7 +90,7 @@ public class DnsUtils {
|
||||
// queued up for publishing, not when it is actually published by the DNS
|
||||
// writer. This timestamp acts as a cooldown so the same request will not be
|
||||
// retried too frequently. See DnsRefreshRequest.getLastProcessTime for a
|
||||
// detailed explaination.
|
||||
// detailed explanation.
|
||||
.map(e -> e.updateProcessTime(transactionTime))
|
||||
.collect(toImmutableList());
|
||||
tm().updateAll(requests);
|
||||
@@ -119,7 +104,7 @@ public class DnsUtils {
|
||||
* <p>Note that if a request entity has already been deleted, the method still succeeds without
|
||||
* error because all we care about is that it no longer exists after the method runs.
|
||||
*/
|
||||
public void deleteRequests(Collection<DnsRefreshRequest> requests) {
|
||||
public static void deleteRequests(Collection<DnsRefreshRequest> requests) {
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().delete(
|
||||
@@ -140,8 +125,9 @@ public class DnsUtils {
|
||||
return dnsAPlusAaaaTtl.getStandardSeconds();
|
||||
}
|
||||
|
||||
private boolean usePullQueue() {
|
||||
return !DatabaseMigrationStateSchedule.getValueAtTime(dnsQueue.getClock().nowUtc())
|
||||
.equals(MigrationState.DNS_SQL);
|
||||
/** The possible values of the {@code DNS_TARGET_TYPE_PARAM} parameter. */
|
||||
public enum TargetType {
|
||||
DOMAIN,
|
||||
HOST
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_WRITER;
|
||||
import static google.registry.dns.DnsModule.PARAM_DOMAINS;
|
||||
import static google.registry.dns.DnsModule.PARAM_HOSTS;
|
||||
@@ -23,6 +22,9 @@ import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_TIME;
|
||||
import static google.registry.dns.DnsUtils.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
@@ -85,7 +87,6 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final DnsUtils dnsUtils;
|
||||
private final DnsWriterProxy dnsWriterProxy;
|
||||
private final DnsMetrics dnsMetrics;
|
||||
private final Duration timeout;
|
||||
@@ -94,10 +95,10 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
/**
|
||||
* The DNS writer to use for this batch.
|
||||
*
|
||||
* <p>This comes from the fanout in {@link ReadDnsQueueAction} which dispatches each batch to be
|
||||
* published by each DNS writer on the TLD. So this field contains the value of one of the DNS
|
||||
* writers configured in {@link Tld#getDnsWriters()}, as of the time the batch was written out
|
||||
* (and not necessarily currently).
|
||||
* <p>This comes from the fanout in {@link ReadDnsRefreshRequestsAction} which dispatches each
|
||||
* batch to be published by each DNS writer on the TLD. So this field contains the value of one of
|
||||
* the DNS writers configured in {@link Tld#getDnsWriters()}, as of the time the batch was written
|
||||
* out (and not necessarily currently).
|
||||
*/
|
||||
private final String dnsWriter;
|
||||
|
||||
@@ -139,7 +140,6 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress gSuiteOutgoingEmailAddress,
|
||||
@Header(APP_ENGINE_RETRY_HEADER) Optional<Integer> appEngineRetryCount,
|
||||
@Header(CLOUD_TASKS_RETRY_HEADER) Optional<Integer> cloudTasksRetryCount,
|
||||
DnsUtils dnsUtils,
|
||||
DnsWriterProxy dnsWriterProxy,
|
||||
DnsMetrics dnsMetrics,
|
||||
LockHandler lockHandler,
|
||||
@@ -147,12 +147,11 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
SendEmailService sendEmailService,
|
||||
Response response) {
|
||||
this.dnsUtils = dnsUtils;
|
||||
this.dnsWriterProxy = dnsWriterProxy;
|
||||
this.dnsMetrics = dnsMetrics;
|
||||
this.timeout = timeout;
|
||||
this.sendEmailService = sendEmailService;
|
||||
this.retryCount =
|
||||
retryCount =
|
||||
cloudTasksRetryCount.orElse(
|
||||
appEngineRetryCount.orElseThrow(
|
||||
() -> new IllegalStateException("Missing a valid retry count header")));
|
||||
@@ -276,7 +275,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private InternetAddress emailToInternetAddress(String email) {
|
||||
private static InternetAddress emailToInternetAddress(String email) {
|
||||
try {
|
||||
return new InternetAddress(email, true);
|
||||
} catch (Exception e) {
|
||||
@@ -306,7 +305,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
registrar.get().getContacts().stream()
|
||||
.filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN))
|
||||
.map(RegistrarPoc::getEmailAddress)
|
||||
.map(this::emailToInternetAddress)
|
||||
.map(PublishDnsUpdatesAction::emailToInternetAddress)
|
||||
.collect(toImmutableList());
|
||||
|
||||
sendEmailService.sendEmail(
|
||||
@@ -356,10 +355,10 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
private void requeueBatch() {
|
||||
logger.atInfo().log("Requeueing batch for retry.");
|
||||
for (String domain : nullToEmpty(domains)) {
|
||||
dnsUtils.requestDomainDnsRefresh(domain);
|
||||
requestDomainDnsRefresh(domain);
|
||||
}
|
||||
for (String host : nullToEmpty(hosts)) {
|
||||
dnsUtils.requestHostDnsRefresh(host);
|
||||
requestHostDnsRefresh(host);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_CREATE_TIME_PARAM;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_WRITER;
|
||||
import static google.registry.dns.DnsModule.PARAM_DOMAINS;
|
||||
import static google.registry.dns.DnsModule.PARAM_HOSTS;
|
||||
import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_TIME;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.util.DomainNameUtils.getSecondLevelDomain;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.appengine.api.taskqueue.TaskHandle;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.tld.Registries;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* Action for fanning out DNS refresh tasks by TLD, using data taken from the DNS pull queue.
|
||||
*
|
||||
* <h3>Parameters Reference</h3>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code jitterSeconds} Randomly delay each task by up to this many seconds.
|
||||
* </ul>
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.BACKEND,
|
||||
path = "/_dr/cron/readDnsQueue",
|
||||
automaticallyPrintOk = true,
|
||||
auth = Auth.AUTH_INTERNAL_OR_ADMIN)
|
||||
public final class ReadDnsQueueAction implements Runnable {
|
||||
|
||||
private static final String PARAM_JITTER_SECONDS = "jitterSeconds";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
* Buffer time since the end of this action until retriable tasks are available again.
|
||||
*
|
||||
* <p>We read batches of tasks from the queue in a loop. Any task that will need to be retried has
|
||||
* to be kept out of the queue for the duration of this action - otherwise we will lease it again
|
||||
* in a subsequent loop.
|
||||
*
|
||||
* <p>The 'requestedMaximumDuration' value is the maximum delay between the first and last calls
|
||||
* to lease tasks, hence we want the lease duration to be (slightly) longer than that.
|
||||
* LEASE_PADDING is the value we add to {@link #requestedMaximumDuration} to make sure the lease
|
||||
* duration is indeed longer.
|
||||
*/
|
||||
private static final Duration LEASE_PADDING = Duration.standardMinutes(1);
|
||||
|
||||
private final int tldUpdateBatchSize;
|
||||
private final Duration requestedMaximumDuration;
|
||||
private final Optional<Integer> jitterSeconds;
|
||||
private final Clock clock;
|
||||
private final DnsQueue dnsQueue;
|
||||
private final HashFunction hashFunction;
|
||||
private final CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject
|
||||
ReadDnsQueueAction(
|
||||
@Config("dnsTldUpdateBatchSize") int tldUpdateBatchSize,
|
||||
@Config("readDnsRefreshRequestsActionRuntime") Duration requestedMaximumDuration,
|
||||
@Parameter(PARAM_JITTER_SECONDS) Optional<Integer> jitterSeconds,
|
||||
Clock clock,
|
||||
DnsQueue dnsQueue,
|
||||
HashFunction hashFunction,
|
||||
CloudTasksUtils cloudTasksUtils) {
|
||||
this.tldUpdateBatchSize = tldUpdateBatchSize;
|
||||
this.requestedMaximumDuration = requestedMaximumDuration;
|
||||
this.jitterSeconds = jitterSeconds;
|
||||
this.clock = clock;
|
||||
this.dnsQueue = dnsQueue;
|
||||
this.hashFunction = hashFunction;
|
||||
this.cloudTasksUtils = cloudTasksUtils;
|
||||
}
|
||||
|
||||
/** Container for items we pull out of the DNS pull queue and process for fanout. */
|
||||
@AutoValue
|
||||
abstract static class RefreshItem implements Comparable<RefreshItem> {
|
||||
static RefreshItem create(TargetType type, String name, DateTime creationTime) {
|
||||
return new AutoValue_ReadDnsQueueAction_RefreshItem(type, name, creationTime);
|
||||
}
|
||||
|
||||
abstract TargetType type();
|
||||
|
||||
abstract String name();
|
||||
|
||||
abstract DateTime creationTime();
|
||||
|
||||
@Override
|
||||
public int compareTo(RefreshItem other) {
|
||||
return ComparisonChain.start()
|
||||
.compare(this.type(), other.type())
|
||||
.compare(this.name(), other.name())
|
||||
.compare(this.creationTime(), other.creationTime())
|
||||
.result();
|
||||
}
|
||||
}
|
||||
|
||||
/** Leases all tasks from the pull queue and creates per-tld update actions for them. */
|
||||
@Override
|
||||
public void run() {
|
||||
DateTime requestedEndTime = clock.nowUtc().plus(requestedMaximumDuration);
|
||||
ImmutableSet<String> tlds = Registries.getTlds();
|
||||
while (requestedEndTime.isAfterNow()) {
|
||||
List<TaskHandle> tasks = dnsQueue.leaseTasks(requestedMaximumDuration.plus(LEASE_PADDING));
|
||||
logger.atInfo().log("Leased %d DNS update tasks.", tasks.size());
|
||||
if (!tasks.isEmpty()) {
|
||||
dispatchTasks(ImmutableSet.copyOf(tasks), tlds);
|
||||
}
|
||||
if (tasks.size() < dnsQueue.getLeaseTasksBatchSize()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A set of tasks grouped based on the action to take on them. */
|
||||
@AutoValue
|
||||
abstract static class ClassifiedTasks {
|
||||
|
||||
/**
|
||||
* List of tasks we want to keep in the queue (want to retry in the future).
|
||||
*
|
||||
* <p>Normally, any task we lease from the queue will be deleted - either because we are going
|
||||
* to process it now (these tasks are part of refreshItemsByTld), or because these tasks are
|
||||
* "corrupt" in some way (don't parse, don't have the required parameters etc.).
|
||||
*
|
||||
* <p>Some tasks however are valid, but can't be processed at the moment. These tasks will be
|
||||
* kept in the queue for future processing.
|
||||
*
|
||||
* <p>This includes tasks belonging to paused TLDs (which we want to process once the TLD is
|
||||
* unpaused) and tasks belonging to (currently) unknown TLDs.
|
||||
*/
|
||||
abstract ImmutableSet<TaskHandle> tasksToKeep();
|
||||
|
||||
/** The paused TLDs for which we found at least one refresh request. */
|
||||
abstract ImmutableSet<String> pausedTlds();
|
||||
|
||||
/**
|
||||
* The unknown TLDs for which we found at least one refresh request.
|
||||
*
|
||||
* <p>Unknown TLDs might have valid requests because the list of TLDs is heavily cached. Hence,
|
||||
* when we add a new TLD - it is possible that some instances will not have that TLD in their
|
||||
* list yet. We don't want to discard these tasks, so we wait a bit and retry them again.
|
||||
*
|
||||
* <p>This is less likely for production TLDs but is quite likely for test TLDs where we might
|
||||
* create a TLD and then use it within seconds.
|
||||
*/
|
||||
abstract ImmutableSet<String> unknownTlds();
|
||||
|
||||
/**
|
||||
* All the refresh items we need to actually fulfill, grouped by TLD.
|
||||
*
|
||||
* <p>By default, the multimap is ordered - this can be changed with the builder.
|
||||
*
|
||||
* <p>The items for each TLD will be grouped together, and domains and hosts will be grouped
|
||||
* within a TLD.
|
||||
*
|
||||
* <p>The grouping and ordering of domains and hosts is not technically necessary, but a
|
||||
* predictable ordering makes it possible to write detailed tests.
|
||||
*/
|
||||
abstract ImmutableSetMultimap<String, RefreshItem> refreshItemsByTld();
|
||||
|
||||
static Builder builder() {
|
||||
Builder builder = new AutoValue_ReadDnsQueueAction_ClassifiedTasks.Builder();
|
||||
builder
|
||||
.refreshItemsByTldBuilder()
|
||||
.orderKeysBy(Ordering.natural())
|
||||
.orderValuesBy(Ordering.natural());
|
||||
return builder;
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
abstract static class Builder {
|
||||
abstract ImmutableSet.Builder<TaskHandle> tasksToKeepBuilder();
|
||||
abstract ImmutableSet.Builder<String> pausedTldsBuilder();
|
||||
abstract ImmutableSet.Builder<String> unknownTldsBuilder();
|
||||
abstract ImmutableSetMultimap.Builder<String, RefreshItem> refreshItemsByTldBuilder();
|
||||
|
||||
abstract ClassifiedTasks build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates per-tld update actions for tasks leased from the pull queue.
|
||||
*
|
||||
* <p>Will return "irrelevant" tasks to the queue for future processing. "Irrelevant" tasks are
|
||||
* tasks for paused TLDs or tasks for TLDs not part of {@link Registries#getTlds()}.
|
||||
*/
|
||||
private void dispatchTasks(ImmutableSet<TaskHandle> tasks, ImmutableSet<String> tlds) {
|
||||
ClassifiedTasks classifiedTasks = classifyTasks(tasks, tlds);
|
||||
if (!classifiedTasks.pausedTlds().isEmpty()) {
|
||||
logger.atInfo().log(
|
||||
"The dns-pull queue is paused for TLDs: %s.", classifiedTasks.pausedTlds());
|
||||
}
|
||||
if (!classifiedTasks.unknownTlds().isEmpty()) {
|
||||
logger.atWarning().log(
|
||||
"The dns-pull queue has unknown TLDs: %s.", classifiedTasks.unknownTlds());
|
||||
}
|
||||
bucketRefreshItems(classifiedTasks.refreshItemsByTld());
|
||||
if (!classifiedTasks.tasksToKeep().isEmpty()) {
|
||||
logger.atWarning().log(
|
||||
"Keeping %d DNS update tasks in the queue.", classifiedTasks.tasksToKeep().size());
|
||||
}
|
||||
// Delete the tasks we don't want to see again from the queue.
|
||||
//
|
||||
// tasksToDelete includes both the tasks that we already fulfilled in this call (were part of
|
||||
// refreshItemsByTld) and "corrupt" tasks we weren't able to parse correctly (this shouldn't
|
||||
// happen, and we logged a "severe" error)
|
||||
//
|
||||
// We let the lease on the rest of the tasks (the tasks we want to keep) expire on its own. The
|
||||
// reason we don't release these tasks back to the queue immediately is that we are going to
|
||||
// immediately read another batch of tasks from the queue - and we don't want to get the same
|
||||
// tasks again.
|
||||
ImmutableSet<TaskHandle> tasksToDelete =
|
||||
difference(tasks, classifiedTasks.tasksToKeep()).immutableCopy();
|
||||
logger.atInfo().log("Removing %d DNS update tasks from the queue.", tasksToDelete.size());
|
||||
dnsQueue.deleteTasks(tasksToDelete.asList());
|
||||
logger.atInfo().log("Done processing DNS tasks.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Classifies the given tasks based on what action we need to take on them.
|
||||
*
|
||||
* <p>Note that some tasks might appear in multiple categories (if multiple actions are to be
|
||||
* taken on them) or in no category (if no action is to be taken on them)
|
||||
*/
|
||||
private static ClassifiedTasks classifyTasks(
|
||||
ImmutableSet<TaskHandle> tasks, ImmutableSet<String> tlds) {
|
||||
|
||||
ClassifiedTasks.Builder classifiedTasksBuilder = ClassifiedTasks.builder();
|
||||
|
||||
// Read all tasks on the DNS pull queue and load them into the refresh item multimap.
|
||||
for (TaskHandle task : tasks) {
|
||||
try {
|
||||
Map<String, String> params = ImmutableMap.copyOf(task.extractParams());
|
||||
DateTime creationTime = DateTime.parse(params.get(DNS_TARGET_CREATE_TIME_PARAM));
|
||||
String tld = params.get(PARAM_TLD);
|
||||
if (tld == null) {
|
||||
logger.atSevere().log(
|
||||
"Discarding invalid DNS refresh request %s; no TLD specified.", task);
|
||||
} else if (!tlds.contains(tld)) {
|
||||
classifiedTasksBuilder.tasksToKeepBuilder().add(task);
|
||||
classifiedTasksBuilder.unknownTldsBuilder().add(tld);
|
||||
} else if (Tld.get(tld).getDnsPaused()) {
|
||||
classifiedTasksBuilder.tasksToKeepBuilder().add(task);
|
||||
classifiedTasksBuilder.pausedTldsBuilder().add(tld);
|
||||
} else {
|
||||
String typeString = params.get(DNS_TARGET_TYPE_PARAM);
|
||||
String name = params.get(DNS_TARGET_NAME_PARAM);
|
||||
TargetType type = TargetType.valueOf(typeString);
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
case HOST:
|
||||
classifiedTasksBuilder
|
||||
.refreshItemsByTldBuilder()
|
||||
.put(tld, RefreshItem.create(type, name, creationTime));
|
||||
break;
|
||||
default:
|
||||
logger.atSevere().log(
|
||||
"Discarding DNS refresh request %s of type %s.", task, typeString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException | UnsupportedEncodingException e) {
|
||||
logger.atSevere().withCause(e).log("Discarding invalid DNS refresh request %s.", task);
|
||||
}
|
||||
}
|
||||
return classifiedTasksBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subdivides the tld to {@link RefreshItem} multimap into buckets by lock index, if applicable.
|
||||
*
|
||||
* <p>If the tld has numDnsPublishLocks <= 1, we enqueue all updates on the default lock 1 of 1.
|
||||
*/
|
||||
private void bucketRefreshItems(ImmutableSetMultimap<String, RefreshItem> refreshItemsByTld) {
|
||||
// Loop through the multimap by TLD and generate refresh tasks for the hosts and domains for
|
||||
// each configured DNS writer.
|
||||
for (Map.Entry<String, Collection<RefreshItem>> tldRefreshItemsEntry
|
||||
: refreshItemsByTld.asMap().entrySet()) {
|
||||
String tld = tldRefreshItemsEntry.getKey();
|
||||
int numPublishLocks = Tld.get(tld).getNumDnsPublishLocks();
|
||||
// 1 lock or less implies no TLD-wide locks, simply enqueue everything under lock 1 of 1
|
||||
if (numPublishLocks <= 1) {
|
||||
enqueueUpdates(tld, 1, 1, tldRefreshItemsEntry.getValue());
|
||||
} else {
|
||||
tldRefreshItemsEntry.getValue().stream()
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
refreshItem -> getLockIndex(tld, numPublishLocks, refreshItem),
|
||||
refreshItem -> refreshItem))
|
||||
.asMap()
|
||||
.forEach((key, value) -> enqueueUpdates(tld, key, numPublishLocks, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lock index for a given refreshItem.
|
||||
*
|
||||
* <p>We hash the second level domain for all records, to group in-bailiwick hosts (the only ones
|
||||
* we refresh DNS for) with their superordinate domains. We use consistent hashing to determine
|
||||
* the lock index because it gives us [0,N) bucketing properties out of the box, then add 1 to
|
||||
* make indexes within [1,N].
|
||||
*/
|
||||
private int getLockIndex(String tld, int numPublishLocks, RefreshItem refreshItem) {
|
||||
String domain = getSecondLevelDomain(refreshItem.name(), tld);
|
||||
return Hashing.consistentHash(hashFunction.hashString(domain, UTF_8), numPublishLocks) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates DNS refresh tasks for all writers for the tld within a lock index and batches large
|
||||
* updates into smaller chunks.
|
||||
*/
|
||||
private void enqueueUpdates(
|
||||
String tld, int lockIndex, int numPublishLocks, Collection<RefreshItem> items) {
|
||||
for (List<RefreshItem> chunk : Iterables.partition(items, tldUpdateBatchSize)) {
|
||||
DateTime earliestCreateTime =
|
||||
chunk.stream().map(RefreshItem::creationTime).min(Comparator.naturalOrder()).get();
|
||||
for (String dnsWriter : Tld.get(tld).getDnsWriters()) {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
PublishDnsUpdatesAction.PATH,
|
||||
Service.BACKEND,
|
||||
ImmutableMultimap.<String, String>builder()
|
||||
.put(PARAM_TLD, tld)
|
||||
.put(PARAM_DNS_WRITER, dnsWriter)
|
||||
.put(PARAM_LOCK_INDEX, Integer.toString(lockIndex))
|
||||
.put(PARAM_NUM_PUBLISH_LOCKS, Integer.toString(numPublishLocks))
|
||||
.put(PARAM_PUBLISH_TASK_ENQUEUED, clock.nowUtc().toString())
|
||||
.put(PARAM_REFRESH_REQUEST_TIME, earliestCreateTime.toString())
|
||||
.put(
|
||||
PARAM_DOMAINS,
|
||||
chunk.stream()
|
||||
.filter(item -> item.type() == TargetType.DOMAIN)
|
||||
.map(RefreshItem::name)
|
||||
.collect(Collectors.joining(",")))
|
||||
.put(
|
||||
PARAM_HOSTS,
|
||||
chunk.stream()
|
||||
.filter(item -> item.type() == TargetType.HOST)
|
||||
.map(RefreshItem::name)
|
||||
.collect(Collectors.joining(",")))
|
||||
.build(),
|
||||
jitterSeconds);
|
||||
cloudTasksUtils.enqueue(DNS_PUBLISH_PUSH_QUEUE_NAME, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_JITTER_SECONDS;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_WRITER;
|
||||
import static google.registry.dns.DnsModule.PARAM_DOMAINS;
|
||||
@@ -24,6 +23,9 @@ import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_TIME;
|
||||
import static google.registry.dns.DnsUtils.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsUtils.deleteRequests;
|
||||
import static google.registry.dns.DnsUtils.readAndUpdateRequestsWithLatestProcessTime;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
@@ -39,7 +41,7 @@ import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.request.Action;
|
||||
@@ -72,7 +74,6 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
private final Optional<Integer> jitterSeconds;
|
||||
private final String tld;
|
||||
private final Clock clock;
|
||||
private final DnsUtils dnsUtils;
|
||||
private final HashFunction hashFunction;
|
||||
private final CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@@ -83,7 +84,6 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
@Parameter(PARAM_DNS_JITTER_SECONDS) Optional<Integer> jitterSeconds,
|
||||
@Parameter(PARAM_TLD) String tld,
|
||||
Clock clock,
|
||||
DnsUtils dnsUtils,
|
||||
HashFunction hashFunction,
|
||||
CloudTasksUtils cloudTasksUtils) {
|
||||
this.tldUpdateBatchSize = tldUpdateBatchSize;
|
||||
@@ -91,7 +91,6 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
this.jitterSeconds = jitterSeconds;
|
||||
this.tld = tld;
|
||||
this.clock = clock;
|
||||
this.dnsUtils = dnsUtils;
|
||||
this.hashFunction = hashFunction;
|
||||
this.cloudTasksUtils = cloudTasksUtils;
|
||||
}
|
||||
@@ -112,7 +111,7 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
int processBatchSize = tldUpdateBatchSize * Tld.get(tld).getNumDnsPublishLocks();
|
||||
while (requestedEndTime.isAfter(clock.nowUtc())) {
|
||||
ImmutableList<DnsRefreshRequest> requests =
|
||||
dnsUtils.readAndUpdateRequestsWithLatestProcessTime(
|
||||
readAndUpdateRequestsWithLatestProcessTime(
|
||||
tld, requestedMaximumDuration, processBatchSize);
|
||||
logger.atInfo().log("Read %d DNS update requests for TLD %s.", requests.size(), tld);
|
||||
if (!requests.isEmpty()) {
|
||||
@@ -138,7 +137,7 @@ public final class ReadDnsRefreshRequestsAction implements Runnable {
|
||||
(lockIndex, bucketedRequests) -> {
|
||||
try {
|
||||
enqueueUpdates(lockIndex, numPublishLocks, bucketedRequests);
|
||||
dnsUtils.deleteRequests(bucketedRequests);
|
||||
deleteRequests(bucketedRequests);
|
||||
logger.atInfo().log(
|
||||
"Processed %d DNS update requests for TLD %s.", bucketedRequests.size(), tld);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.EppResource.ForeignKeyedEppResource;
|
||||
import google.registry.model.annotations.ExternalMessagingName;
|
||||
@@ -39,7 +41,6 @@ import javax.inject.Inject;
|
||||
public final class RefreshDnsAction implements Runnable {
|
||||
|
||||
private final Clock clock;
|
||||
private final DnsUtils dnsUtils;
|
||||
private final String domainOrHostName;
|
||||
private final TargetType type;
|
||||
|
||||
@@ -47,12 +48,10 @@ public final class RefreshDnsAction implements Runnable {
|
||||
RefreshDnsAction(
|
||||
@Parameter("domainOrHostName") String domainOrHostName,
|
||||
@Parameter("type") TargetType type,
|
||||
Clock clock,
|
||||
DnsUtils dnsUtils) {
|
||||
Clock clock) {
|
||||
this.domainOrHostName = domainOrHostName;
|
||||
this.type = type;
|
||||
this.clock = clock;
|
||||
this.dnsUtils = dnsUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,11 +62,11 @@ public final class RefreshDnsAction implements Runnable {
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
loadAndVerifyExistence(Domain.class, domainOrHostName);
|
||||
dnsUtils.requestDomainDnsRefresh(domainOrHostName);
|
||||
requestDomainDnsRefresh(domainOrHostName);
|
||||
break;
|
||||
case HOST:
|
||||
verifyHostIsSubordinate(loadAndVerifyExistence(Host.class, domainOrHostName));
|
||||
dnsUtils.requestHostDnsRefresh(domainOrHostName);
|
||||
requestHostDnsRefresh(domainOrHostName);
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("Unsupported type: " + type);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.dns.RefreshDnsOnHostRenameAction.PATH;
|
||||
import static google.registry.model.EppResourceUtils.getLinkedDomainKeys;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
@@ -45,14 +46,11 @@ public class RefreshDnsOnHostRenameAction implements Runnable {
|
||||
|
||||
private final VKey<Host> hostKey;
|
||||
private final Response response;
|
||||
private final DnsUtils dnsUtils;
|
||||
|
||||
@Inject
|
||||
RefreshDnsOnHostRenameAction(
|
||||
@Parameter(PARAM_HOST_KEY) String hostKey, Response response, DnsUtils dnsUtils) {
|
||||
RefreshDnsOnHostRenameAction(@Parameter(PARAM_HOST_KEY) String hostKey, Response response) {
|
||||
this.hostKey = VKey.createEppVKeyFromString(hostKey);
|
||||
this.response = response;
|
||||
this.dnsUtils = dnsUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,7 +74,7 @@ public class RefreshDnsOnHostRenameAction implements Runnable {
|
||||
.stream()
|
||||
.map(domainKey -> tm().loadByKey(domainKey))
|
||||
.filter(Domain::shouldPublishToDns)
|
||||
.forEach(domain -> dnsUtils.requestDomainDnsRefresh(domain.getDomainName()));
|
||||
.forEach(domain -> requestDomainDnsRefresh(domain.getDomainName()));
|
||||
}
|
||||
|
||||
if (!hostValid) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import dagger.multibindings.IntoSet;
|
||||
import dagger.multibindings.StringKey;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.writer.DnsWriter;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
@@ -35,7 +35,7 @@ public abstract class CloudDnsWriterModule {
|
||||
|
||||
@Provides
|
||||
static Dns provideDns(
|
||||
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("cloudDnsRootUrl") Optional<String> rootUrl,
|
||||
@Config("cloudDnsServicePath") Optional<String> servicePath) {
|
||||
|
||||
+2
-12
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<entries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
@@ -129,16 +129,6 @@
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
@@ -148,4 +138,4 @@
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
</entries>
|
||||
|
||||
@@ -151,12 +151,6 @@
|
||||
<url-pattern>/_dr/task/nordnVerify</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Reads the DNS push and pull queues and kick off the appropriate tasks to update zone. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
<url-pattern>/_dr/cron/readDnsQueue</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- Reads the DNS refresh requests and kick off the appropriate tasks to update zone. -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>backend-servlet</servlet-name>
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<entries>
|
||||
<!-- Queue template with all supported params -->
|
||||
<!-- More information - https://cloud.google.com/sdk/gcloud/reference/tasks/queues/create -->
|
||||
<!--
|
||||
<queue>
|
||||
<name></name>
|
||||
<max-attempts></max-attempts>
|
||||
<max-backoff></max-backoff>
|
||||
<max-concurrent-dispatches></max-concurrent-dispatches>
|
||||
<max-dispatches-per-second></max-dispatches-per-second>
|
||||
<max-doublings></max-doublings>
|
||||
<max-retry-duration></max-retry-duration>
|
||||
<min-backoff></min-backoff>
|
||||
</queue>
|
||||
-->
|
||||
|
||||
<!-- Queue for reading DNS update requests and batching them off to the dns-publish queue. -->
|
||||
<queue>
|
||||
<name>dns-refresh</name>
|
||||
<max-dispatches-per-second>100</max-dispatches-per-second>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for publishing DNS updates in batches. -->
|
||||
<queue>
|
||||
<name>dns-publish</name>
|
||||
<max-dispatches-per-second>100</max-dispatches-per-second>
|
||||
<!-- 30 sec backoff increasing linearly up to 30 minutes. -->
|
||||
<min-backoff>30s</min-backoff>
|
||||
<max-backoff>1800s</max-backoff>
|
||||
<max-doublings>0</max-doublings>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for uploading RDE deposits to the escrow provider. -->
|
||||
<queue>
|
||||
<name>rde-upload</name>
|
||||
<max-dispatches-per-second>0.166666667</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>5</max-concurrent-dispatches>
|
||||
<max-retry-duration>14400s</max-retry-duration>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for uploading RDE reports to ICANN. -->
|
||||
<queue>
|
||||
<name>rde-report</name>
|
||||
<max-dispatches-per-second>1</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>1</max-concurrent-dispatches>
|
||||
<max-retry-duration>14400s</max-retry-duration>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for copying BRDA deposits to GCS. -->
|
||||
<queue>
|
||||
<name>brda</name>
|
||||
<max-dispatches-per-second>0.016666667</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>10</max-concurrent-dispatches>
|
||||
<max-retry-duration>82800s</max-retry-duration>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that trigger domain DNS update upon host rename. -->
|
||||
<queue>
|
||||
<name>async-host-rename</name>
|
||||
<max-dispatches-per-second>1</max-dispatches-per-second>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that wait for a Beam pipeline to complete (i.e. Spec11 and invoicing). -->
|
||||
<queue>
|
||||
<name>beam-reporting</name>
|
||||
<max-dispatches-per-second>0.016666667</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>1</max-concurrent-dispatches>
|
||||
<max-attempts>5</max-attempts>
|
||||
<min-backoff>180s</min-backoff>
|
||||
<max-backoff>180s</max-backoff>
|
||||
</queue>
|
||||
|
||||
|
||||
<!-- Queue for tasks that communicate with TMCH MarksDB webserver. -->
|
||||
<queue>
|
||||
<name>marksdb</name>
|
||||
<max-dispatches-per-second>0.016666667</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>1</max-concurrent-dispatches>
|
||||
<max-retry-duration>39600s</max-retry-duration> <!-- cron interval minus hour -->
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks to produce LORDN CSV reports, populated by a Cloud Scheduler fanout job. -->
|
||||
<queue>
|
||||
<name>nordn</name>
|
||||
<max-dispatches-per-second>1</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>10</max-concurrent-dispatches>
|
||||
<max-retry-duration>39600s</max-retry-duration> <!-- cron interval minus hour -->
|
||||
</queue>
|
||||
|
||||
<!-- Queue for tasks that sync data to Google Spreadsheets. -->
|
||||
<queue>
|
||||
<name>sheet</name>
|
||||
<max-dispatches-per-second>1</max-dispatches-per-second>
|
||||
<!-- max-concurrent-dispatches is intentionally omitted. -->
|
||||
<max-retry-duration>3600s</max-retry-duration>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for infrequent cron tasks (i.e. hourly or less often) that should retry three times on failure. -->
|
||||
<queue>
|
||||
<name>retryable-cron-tasks</name>
|
||||
<max-dispatches-per-second>1</max-dispatches-per-second>
|
||||
<max-attempts>3</max-attempts>
|
||||
</queue>
|
||||
|
||||
<!-- <!– Queue for async actions that should be run at some point in the future. –>-->
|
||||
<queue>
|
||||
<name>async-actions</name>
|
||||
<max-dispatches-per-second>1</max-dispatches-per-second>
|
||||
<max-concurrent-dispatches>5</max-concurrent-dispatches>
|
||||
</queue>
|
||||
|
||||
</entries>
|
||||
+1
-5
@@ -1,11 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- TODO: @ptkach - Delete once Cloud Api deployer is up and running -->
|
||||
<queue-entries>
|
||||
|
||||
<queue>
|
||||
<name>dns-pull</name>
|
||||
<mode>pull</mode>
|
||||
</queue>
|
||||
|
||||
<!-- Queue for reading DNS update requests and batching them off to the dns-publish queue. -->
|
||||
<queue>
|
||||
<name>dns-refresh</name>
|
||||
+2
-12
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<entries>
|
||||
|
||||
<!--
|
||||
/cron/fanout params:
|
||||
@@ -129,16 +129,6 @@
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
@@ -158,4 +148,4 @@
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
</entries>
|
||||
|
||||
Vendored
+2
-12
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<entries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
@@ -201,16 +201,6 @@
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
@@ -283,4 +273,4 @@
|
||||
</description>
|
||||
<schedule>0 15 * * 1</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
</entries>
|
||||
|
||||
+2
-12
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<entries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
@@ -11,16 +11,6 @@
|
||||
<schedule>7 0 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
@@ -78,4 +68,4 @@
|
||||
</description>
|
||||
<schedule>7 3 * * 6</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
</entries>
|
||||
|
||||
+2
-12
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taskentries>
|
||||
<entries>
|
||||
<task>
|
||||
<url>/_dr/task/rdeStaging</url>
|
||||
<name>rdeStaging</name>
|
||||
@@ -143,16 +143,6 @@
|
||||
<schedule>0 5 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/cron/readDnsQueue?jitterSeconds=45]]></url>
|
||||
<name>readDnsQueue</name>
|
||||
<description>
|
||||
Lease all tasks from the dns-pull queue, group by TLD, and invoke PublishDnsUpdates for each
|
||||
group.
|
||||
</description>
|
||||
<schedule>*/1 * * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url>
|
||||
<![CDATA[/_dr/cron/fanout?queue=dns-refresh&forEachRealTld&forEachTestTld&endpoint=/_dr/task/readDnsRefreshRequests&dnsJitterSeconds=45]]></url>
|
||||
@@ -172,4 +162,4 @@
|
||||
</description>
|
||||
<schedule>0 15 * * 1</schedule>
|
||||
</task>
|
||||
</taskentries>
|
||||
</entries>
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
||||
@@ -59,7 +60,6 @@ import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
@@ -227,7 +227,6 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
@Inject DomainCreateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainFlowTmchUtils tmchUtils;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
|
||||
@Inject DomainCreateFlow() {}
|
||||
|
||||
@@ -426,7 +425,7 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
allocationToken.get(), domainHistory.getHistoryEntryId()));
|
||||
}
|
||||
if (domain.shouldPublishToDns()) {
|
||||
dnsUtils.requestDomainDnsRefresh(domain.getDomainName());
|
||||
requestDomainDnsRefresh(domain.getDomainName());
|
||||
}
|
||||
EntityChanges entityChanges =
|
||||
flowCustomLogic.beforeSave(
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.flows.domain;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.createHistoryEntryId;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
@@ -44,7 +45,6 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import google.registry.batch.AsyncTaskEnqueuer;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.AssociationProhibitsOperationException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
@@ -130,7 +130,6 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject Trid trid;
|
||||
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@@ -262,7 +261,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
// If there's a pending transfer, the gaining client's autorenew billing
|
||||
// event and poll message will already have been deleted in
|
||||
// ResourceDeleteFlow since it's listed in serverApproveEntities.
|
||||
dnsUtils.requestDomainDnsRefresh(existingDomain.getDomainName());
|
||||
requestDomainDnsRefresh(existingDomain.getDomainName());
|
||||
|
||||
entitiesToSave.add(newDomain, domainHistory);
|
||||
EntityChanges entityChanges =
|
||||
@@ -431,7 +430,7 @@ public final class DomainDeleteFlow implements TransactionalFlow {
|
||||
|
||||
/** Domain to be deleted has subordinate hosts. */
|
||||
static class DomainToDeleteHasHostsException extends AssociationProhibitsOperationException {
|
||||
public DomainToDeleteHasHostsException() {
|
||||
DomainToDeleteHasHostsException() {
|
||||
super("Domain to be deleted has subordinate hosts");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.domain;
|
||||
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.createHistoryEntryId;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
@@ -34,7 +35,6 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
@@ -122,7 +122,6 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@Inject DomainRestoreRequestFlow() {}
|
||||
@@ -185,7 +184,7 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
entitiesToSave.add(newDomain, domainHistory, autorenewEvent, autorenewPollMessage);
|
||||
tm().putAll(entitiesToSave.build());
|
||||
tm().delete(existingDomain.getDeletePollMessage());
|
||||
dnsUtils.requestDomainDnsRefresh(existingDomain.getDomainName());
|
||||
requestDomainDnsRefresh(existingDomain.getDomainName());
|
||||
return responseBuilder
|
||||
.setExtensions(createResponseExtensions(feesAndCredits, feeUpdate, isExpired))
|
||||
.build();
|
||||
@@ -305,14 +304,14 @@ public final class DomainRestoreRequestFlow implements TransactionalFlow {
|
||||
|
||||
/** Restore command cannot have other changes specified. */
|
||||
static class RestoreCommandIncludesChangesException extends CommandUseErrorException {
|
||||
public RestoreCommandIncludesChangesException() {
|
||||
RestoreCommandIncludesChangesException() {
|
||||
super("Restore command cannot have other changes specified");
|
||||
}
|
||||
}
|
||||
|
||||
/** Domain is not eligible for restore. */
|
||||
static class DomainNotEligibleForRestoreException extends StatusProhibitsOperationException {
|
||||
public DomainNotEligibleForRestoreException() {
|
||||
DomainNotEligibleForRestoreException() {
|
||||
super("Domain is not eligible for restore");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
|
||||
import static com.google.common.collect.Sets.symmetricDifference;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkSameValuesNotAddedAndRemoved;
|
||||
@@ -49,7 +50,6 @@ import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.RegistrarId;
|
||||
@@ -156,7 +156,6 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject Trid trid;
|
||||
@Inject DomainHistory.Builder historyBuilder;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject DomainUpdateFlowCustomLogic flowCustomLogic;
|
||||
@Inject DomainPricingLogic pricingLogic;
|
||||
@@ -183,7 +182,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
|
||||
validateNewState(newDomain);
|
||||
if (requiresDnsUpdate(existingDomain, newDomain)) {
|
||||
dnsUtils.requestDomainDnsRefresh(targetId);
|
||||
requestDomainDnsRefresh(targetId);
|
||||
}
|
||||
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
|
||||
entitiesToSave.add(newDomain, domainHistory);
|
||||
@@ -207,7 +206,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
}
|
||||
|
||||
/** Determines if any of the changes to new domain should trigger DNS update. */
|
||||
private boolean requiresDnsUpdate(Domain existingDomain, Domain newDomain) {
|
||||
private static boolean requiresDnsUpdate(Domain existingDomain, Domain newDomain) {
|
||||
return existingDomain.shouldPublishToDns() != newDomain.shouldPublishToDns()
|
||||
|| !Objects.equals(newDomain.getDsData(), existingDomain.getDsData())
|
||||
|| !Objects.equals(newDomain.getNsHosts(), existingDomain.getNsHosts());
|
||||
@@ -256,7 +255,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
// We have to verify no duplicate contacts _before_ constructing the domain because it is
|
||||
// illegal to construct a domain with duplicate contacts.
|
||||
Sets.SetView<DesignatedContact> newContacts =
|
||||
Sets.union(Sets.difference(domain.getContacts(), remove.getContacts()), add.getContacts());
|
||||
union(Sets.difference(domain.getContacts(), remove.getContacts()), add.getContacts());
|
||||
validateNoDuplicateContacts(newContacts);
|
||||
|
||||
Domain.Builder domainBuilder =
|
||||
@@ -301,7 +300,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
return domainBuilder.build();
|
||||
}
|
||||
|
||||
private void validateRegistrantIsntBeingRemoved(Change change) throws EppException {
|
||||
private static void validateRegistrantIsntBeingRemoved(Change change) throws EppException {
|
||||
if (change.getRegistrantContactId() != null && change.getRegistrantContactId().isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
@@ -314,7 +313,7 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
* compliant with the additions or amendments, otherwise existing data can become invalid and
|
||||
* cause Domain update failure.
|
||||
*/
|
||||
private void validateNewState(Domain newDomain) throws EppException {
|
||||
private static void validateNewState(Domain newDomain) throws EppException {
|
||||
validateRequiredContactsPresent(newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
@@ -368,17 +367,17 @@ public final class DomainUpdateFlow implements TransactionalFlow {
|
||||
.collect(toImmutableSortedSet(Ordering.natural()));
|
||||
|
||||
String msg;
|
||||
if (addedServerStatuses.size() > 0 && removedServerStatuses.size() > 0) {
|
||||
if (!addedServerStatuses.isEmpty() && !removedServerStatuses.isEmpty()) {
|
||||
msg =
|
||||
String.format(
|
||||
"The registry administrator has added the status(es) %s and removed the status(es)"
|
||||
+ " %s.",
|
||||
addedServerStatuses, removedServerStatuses);
|
||||
} else if (addedServerStatuses.size() > 0) {
|
||||
} else if (!addedServerStatuses.isEmpty()) {
|
||||
msg =
|
||||
String.format(
|
||||
"The registry administrator has added the status(es) %s.", addedServerStatuses);
|
||||
} else if (removedServerStatuses.size() > 0) {
|
||||
} else if (!removedServerStatuses.isEmpty()) {
|
||||
msg =
|
||||
String.format(
|
||||
"The registry administrator has removed the status(es) %s.", removedServerStatuses);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.host;
|
||||
|
||||
import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist;
|
||||
import static google.registry.flows.host.HostFlowUtils.lookupSuperordinateDomain;
|
||||
@@ -28,7 +29,6 @@ import static google.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
@@ -85,7 +85,6 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
@Inject @RegistrarId String registrarId;
|
||||
@Inject @TargetId String targetId;
|
||||
@Inject HostHistory.Builder historyBuilder;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
|
||||
@Inject
|
||||
@@ -138,7 +137,7 @@ public final class HostCreateFlow implements TransactionalFlow {
|
||||
.build());
|
||||
// Only update DNS if this is a subordinate host. External hosts have no glue to write, so
|
||||
// they are only written as NS records from the referencing domain.
|
||||
dnsUtils.requestHostDnsRefresh(targetId);
|
||||
requestHostDnsRefresh(targetId);
|
||||
}
|
||||
tm().insertAll(entitiesToSave);
|
||||
return responseBuilder.setResData(HostCreateData.create(targetId, now)).build();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.host;
|
||||
|
||||
import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkLinkedDomains;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
@@ -24,7 +25,6 @@ import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.RegistrarId;
|
||||
@@ -71,7 +71,6 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_DELETE_PROHIBITED);
|
||||
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject @RegistrarId String registrarId;
|
||||
@Inject @TargetId String targetId;
|
||||
@@ -104,7 +103,7 @@ public final class HostDeleteFlow implements TransactionalFlow {
|
||||
}
|
||||
Host newHost = existingHost.asBuilder().setStatusValues(null).setDeletionTime(now).build();
|
||||
if (existingHost.isSubordinate()) {
|
||||
dnsUtils.requestHostDnsRefresh(existingHost.getHostName());
|
||||
requestHostDnsRefresh(existingHost.getHostName());
|
||||
tm().update(
|
||||
tm().loadByKey(existingHost.getSuperordinateDomain())
|
||||
.asBuilder()
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.flows.host;
|
||||
|
||||
import static com.google.common.base.MoreObjects.firstNonNull;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
|
||||
import static google.registry.dns.RefreshDnsOnHostRenameAction.PARAM_HOST_KEY;
|
||||
import static google.registry.dns.RefreshDnsOnHostRenameAction.QUEUE_HOST_RENAME;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
@@ -37,7 +38,6 @@ import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.batch.AsyncTaskEnqueuer;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.dns.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.ObjectAlreadyExistsException;
|
||||
@@ -124,7 +124,6 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
@Inject @Superuser boolean isSuperuser;
|
||||
@Inject HostHistory.Builder historyBuilder;
|
||||
@Inject AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject EppResponse.Builder responseBuilder;
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@@ -241,7 +240,7 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
|
||||
}
|
||||
|
||||
private void verifyHasIpsIffIsExternal(Update command, Host existingHost, Host newHost)
|
||||
private static void verifyHasIpsIffIsExternal(Update command, Host existingHost, Host newHost)
|
||||
throws EppException {
|
||||
boolean wasSubordinate = existingHost.isSubordinate();
|
||||
boolean willBeSubordinate = newHost.isSubordinate();
|
||||
@@ -266,14 +265,14 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
// Only update DNS for subordinate hosts. External hosts have no glue to write, so they
|
||||
// are only written as NS records from the referencing domain.
|
||||
if (existingHost.isSubordinate()) {
|
||||
dnsUtils.requestHostDnsRefresh(existingHost.getHostName());
|
||||
requestHostDnsRefresh(existingHost.getHostName());
|
||||
}
|
||||
// In case of a rename, there are many updates we need to queue up.
|
||||
if (((Update) resourceCommand).getInnerChange().getHostName() != null) {
|
||||
// If the renamed host is also subordinate, then we must enqueue an update to write the new
|
||||
// glue.
|
||||
if (newHost.isSubordinate()) {
|
||||
dnsUtils.requestHostDnsRefresh(newHost.getHostName());
|
||||
requestHostDnsRefresh(newHost.getHostName());
|
||||
}
|
||||
// We must also enqueue updates for all domains that use this host as their nameserver so
|
||||
// that their NS records can be updated to point at the new name.
|
||||
@@ -317,14 +316,14 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
|
||||
/** Host with specified name already exists. */
|
||||
static class HostAlreadyExistsException extends ObjectAlreadyExistsException {
|
||||
public HostAlreadyExistsException(String hostName) {
|
||||
HostAlreadyExistsException(String hostName) {
|
||||
super(String.format("Object with given ID (%s) already exists", hostName));
|
||||
}
|
||||
}
|
||||
|
||||
/** Cannot add IP addresses to an external host. */
|
||||
static class CannotAddIpToExternalHostException extends ParameterValueRangeErrorException {
|
||||
public CannotAddIpToExternalHostException() {
|
||||
CannotAddIpToExternalHostException() {
|
||||
super("Cannot add IP addresses to external hosts");
|
||||
}
|
||||
}
|
||||
@@ -332,14 +331,14 @@ public final class HostUpdateFlow implements TransactionalFlow {
|
||||
/** Cannot remove all IP addresses from a subordinate host. */
|
||||
static class CannotRemoveSubordinateHostLastIpException
|
||||
extends StatusProhibitsOperationException {
|
||||
public CannotRemoveSubordinateHostLastIpException() {
|
||||
CannotRemoveSubordinateHostLastIpException() {
|
||||
super("Cannot remove all IP addresses from a subordinate host");
|
||||
}
|
||||
}
|
||||
|
||||
/** Cannot rename an external host. */
|
||||
static class CannotRenameExternalHostException extends StatusProhibitsOperationException {
|
||||
public CannotRenameExternalHostException() {
|
||||
CannotRenameExternalHostException() {
|
||||
super("Cannot rename an external host");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -64,7 +64,7 @@ public class GcsUtils implements Serializable {
|
||||
}
|
||||
|
||||
@Inject
|
||||
public GcsUtils(@DefaultCredential GoogleCredentialsBundle credentialsBundle) {
|
||||
public GcsUtils(@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle) {
|
||||
this(
|
||||
StorageOptions.newBuilder()
|
||||
.setCredentials(credentialsBundle.getGoogleCredentials())
|
||||
|
||||
@@ -403,7 +403,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
public static ImmutableMap<VKey<? extends EppResource>, EppResource> loadCached(
|
||||
Iterable<VKey<? extends EppResource>> keys) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return tm().loadByKeys(keys);
|
||||
return tm().transact(() -> tm().loadByKeys(keys));
|
||||
}
|
||||
return ImmutableMap.copyOf(cacheEppResources.getAll(keys));
|
||||
}
|
||||
@@ -416,7 +416,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
*/
|
||||
public static <T extends EppResource> T loadCached(VKey<T> key) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return tm().loadByKey(key);
|
||||
return tm().transact(() -> tm().loadByKey(key));
|
||||
}
|
||||
// Safe to cast because loading a Key<T> returns an entity of type T.
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
// Copyright 2021 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.common;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
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.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 java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.PersistenceException;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* A wrapper object representing the stage-to-time mapping of the Registry 3.0 Cloud SQL migration.
|
||||
*
|
||||
* <p>The entity is stored in SQL throughout the entire migration so as to have a single point of
|
||||
* access.
|
||||
*/
|
||||
@DeleteAfterMigration
|
||||
@Entity
|
||||
public class DatabaseMigrationStateSchedule extends CrossTldSingleton {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static boolean useUncachedForTest = false;
|
||||
|
||||
public enum PrimaryDatabase {
|
||||
CLOUD_SQL,
|
||||
DATASTORE
|
||||
}
|
||||
|
||||
public enum ReplayDirection {
|
||||
NO_REPLAY,
|
||||
DATASTORE_TO_SQL,
|
||||
SQL_TO_DATASTORE
|
||||
}
|
||||
|
||||
/**
|
||||
* The current phase of the migration plus information about which database to use and whether or
|
||||
* 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),
|
||||
|
||||
/** Toggles SQL Sequence based allocateId */
|
||||
SEQUENCE_BASED_ALLOCATE_ID(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Use SQL-based Nordn upload flow instead of the pull queue-based one. */
|
||||
NORDN_SQL(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY),
|
||||
|
||||
/** Use SQL-based DNS update flow instead of the pull queue-based one. */
|
||||
DNS_SQL(PrimaryDatabase.CLOUD_SQL, false, ReplayDirection.NO_REPLAY);
|
||||
|
||||
private final PrimaryDatabase primaryDatabase;
|
||||
private final boolean isReadOnly;
|
||||
private final ReplayDirection replayDirection;
|
||||
|
||||
public PrimaryDatabase getPrimaryDatabase() {
|
||||
return primaryDatabase;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return isReadOnly;
|
||||
}
|
||||
|
||||
public ReplayDirection getReplayDirection() {
|
||||
return replayDirection;
|
||||
}
|
||||
|
||||
MigrationState(
|
||||
PrimaryDatabase primaryDatabase, boolean isReadOnly, ReplayDirection replayDirection) {
|
||||
this.primaryDatabase = primaryDatabase;
|
||||
this.isReadOnly = isReadOnly;
|
||||
this.replayDirection = replayDirection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache of the current migration schedule. The key is meaningless; this is essentially a memoized
|
||||
* Supplier that can be reset for testing purposes and after writes.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static final LoadingCache<
|
||||
Class<DatabaseMigrationStateSchedule>, TimedTransitionProperty<MigrationState>>
|
||||
// Each instance should cache the migration schedule for five minutes before reloading
|
||||
CACHE =
|
||||
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 =
|
||||
createValidStateTransitions();
|
||||
|
||||
/**
|
||||
* The valid state transitions. Generally, one can advance the state one step or move backward any
|
||||
* number of steps, as long as the step we're moving back to has the same primary database as the
|
||||
* one we're in. Otherwise, we must move to the corresponding READ_ONLY stage first.
|
||||
*/
|
||||
private static ImmutableMultimap<MigrationState, MigrationState> createValidStateTransitions() {
|
||||
ImmutableMultimap.Builder<MigrationState, MigrationState> builder =
|
||||
new ImmutableMultimap.Builder<MigrationState, MigrationState>()
|
||||
.put(MigrationState.DATASTORE_ONLY, MigrationState.DATASTORE_PRIMARY)
|
||||
.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(
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.DATASTORE_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY)
|
||||
.putAll(
|
||||
MigrationState.SQL_PRIMARY,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_ONLY)
|
||||
.putAll(
|
||||
MigrationState.SQL_ONLY,
|
||||
MigrationState.SQL_PRIMARY_READ_ONLY,
|
||||
MigrationState.SQL_PRIMARY)
|
||||
.putAll(MigrationState.SQL_ONLY, MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
|
||||
.putAll(MigrationState.SEQUENCE_BASED_ALLOCATE_ID, MigrationState.NORDN_SQL)
|
||||
.putAll(
|
||||
MigrationState.NORDN_SQL,
|
||||
MigrationState.SEQUENCE_BASED_ALLOCATE_ID,
|
||||
MigrationState.DNS_SQL)
|
||||
.putAll(MigrationState.DNS_SQL, MigrationState.NORDN_SQL);
|
||||
|
||||
// In addition, we can always transition from a state to itself (useful when updating the map).
|
||||
Arrays.stream(MigrationState.values()).forEach(state -> builder.put(state, state));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
// Default map to return if we have never saved any -- only use Datastore.
|
||||
@VisibleForTesting
|
||||
public static final TimedTransitionProperty<MigrationState> DEFAULT_TRANSITION_MAP =
|
||||
TimedTransitionProperty.fromValueMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, MigrationState.DATASTORE_ONLY));
|
||||
|
||||
@VisibleForTesting
|
||||
public TimedTransitionProperty<MigrationState> migrationTransitions =
|
||||
TimedTransitionProperty.withInitialValue(MigrationState.DATASTORE_ONLY);
|
||||
|
||||
// Required for Hibernate initialization
|
||||
protected DatabaseMigrationStateSchedule() {}
|
||||
|
||||
@VisibleForTesting
|
||||
public DatabaseMigrationStateSchedule(
|
||||
TimedTransitionProperty<MigrationState> migrationTransitions) {
|
||||
this.migrationTransitions = migrationTransitions;
|
||||
}
|
||||
|
||||
/** Sets and persists to SQL the provided migration transition schedule. */
|
||||
public static void set(ImmutableSortedMap<DateTime, MigrationState> migrationTransitionMap) {
|
||||
tm().assertInTransaction();
|
||||
TimedTransitionProperty<MigrationState> transitions =
|
||||
TimedTransitionProperty.make(
|
||||
migrationTransitionMap,
|
||||
VALID_STATE_TRANSITIONS,
|
||||
"validStateTransitions",
|
||||
MigrationState.DATASTORE_ONLY,
|
||||
"migrationTransitionMap must start with DATASTORE_ONLY");
|
||||
validateTransitionAtCurrentTime(transitions);
|
||||
tm().put(new DatabaseMigrationStateSchedule(transitions));
|
||||
CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void useUncachedForTest() {
|
||||
useUncachedForTest = true;
|
||||
}
|
||||
|
||||
/** Loads the currently-set migration schedule from the cache, or the default if none exists. */
|
||||
public static TimedTransitionProperty<MigrationState> get() {
|
||||
return CACHE.get(DatabaseMigrationStateSchedule.class);
|
||||
}
|
||||
|
||||
/** Returns the database migration status at the given time. */
|
||||
public static MigrationState getValueAtTime(DateTime dateTime) {
|
||||
return useUncachedForTest
|
||||
? getUncached().getValueAtTime(dateTime)
|
||||
: get().getValueAtTime(dateTime);
|
||||
}
|
||||
|
||||
/** Loads the currently-set migration schedule from SQL, or the default if none exists. */
|
||||
@VisibleForTesting
|
||||
static TimedTransitionProperty<MigrationState> getUncached() {
|
||||
return tm().transact(
|
||||
() -> {
|
||||
try {
|
||||
return tm().loadSingleton(DatabaseMigrationStateSchedule.class)
|
||||
.map(s -> s.migrationTransitions)
|
||||
.orElse(DEFAULT_TRANSITION_MAP);
|
||||
} catch (PersistenceException e) {
|
||||
if (!RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)) {
|
||||
throw e;
|
||||
}
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Error when retrieving migration schedule; this should only happen in tests.");
|
||||
return DEFAULT_TRANSITION_MAP;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 must also be valid.
|
||||
*/
|
||||
private static void validateTransitionAtCurrentTime(
|
||||
TimedTransitionProperty<MigrationState> newTransitions) {
|
||||
MigrationState currentValue = getUncached().getValueAtTime(tm().getTransactionTime());
|
||||
MigrationState nextCurrentValue = newTransitions.getValueAtTime(tm().getTransactionTime());
|
||||
checkArgument(
|
||||
VALID_STATE_TRANSITIONS.get(currentValue).contains(nextCurrentValue),
|
||||
"Cannot transition from current state-as-of-now %s to new state-as-of-now %s",
|
||||
currentValue,
|
||||
nextCurrentValue);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.dns.PublishDnsUpdatesAction;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
|
||||
@@ -16,6 +16,10 @@ package google.registry.model.console;
|
||||
|
||||
/** Permissions that users may have in the UI, either per-registrar or globally. */
|
||||
public enum ConsolePermission {
|
||||
/** View basic information about a registrar. */
|
||||
VIEW_REGISTRAR_DETAILS,
|
||||
/** Edit basic information about a registrar. */
|
||||
EDIT_REGISTRAR_DETAILS,
|
||||
/** Add, update, or remove other console users. */
|
||||
MANAGE_USERS,
|
||||
/** Add, update, or remove registrars. */
|
||||
|
||||
@@ -27,6 +27,8 @@ public class ConsoleRoleDefinitions {
|
||||
/** Permissions for a registry support agent. */
|
||||
static final ImmutableSet<ConsolePermission> SUPPORT_AGENT_PERMISSIONS =
|
||||
ImmutableSet.of(
|
||||
ConsolePermission.VIEW_REGISTRAR_DETAILS,
|
||||
ConsolePermission.EDIT_REGISTRAR_DETAILS,
|
||||
ConsolePermission.MANAGE_USERS,
|
||||
ConsolePermission.MANAGE_ACCREDITATION,
|
||||
ConsolePermission.CONFIGURE_EPP_CONNECTION,
|
||||
@@ -69,6 +71,7 @@ public class ConsoleRoleDefinitions {
|
||||
/** Permissions for a registrar partner account manager. */
|
||||
static final ImmutableSet<ConsolePermission> ACCOUNT_MANAGER_PERMISSIONS =
|
||||
ImmutableSet.of(
|
||||
ConsolePermission.VIEW_REGISTRAR_DETAILS,
|
||||
ConsolePermission.DOWNLOAD_DOMAINS,
|
||||
ConsolePermission.VIEW_TLD_PORTFOLIO,
|
||||
ConsolePermission.CONTACT_SUPPORT,
|
||||
@@ -89,6 +92,7 @@ public class ConsoleRoleDefinitions {
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(ACCOUNT_MANAGER_WITH_REGISTRY_LOCK_PERMISSIONS)
|
||||
.add(
|
||||
ConsolePermission.EDIT_REGISTRAR_DETAILS,
|
||||
ConsolePermission.MANAGE_ACCREDITATION,
|
||||
ConsolePermission.CONFIGURE_EPP_CONNECTION,
|
||||
ConsolePermission.CHANGE_NOMULUS_PASSWORD,
|
||||
|
||||
@@ -32,7 +32,6 @@ import google.registry.cron.CronModule;
|
||||
import google.registry.cron.TldFanoutAction;
|
||||
import google.registry.dns.DnsModule;
|
||||
import google.registry.dns.PublishDnsUpdatesAction;
|
||||
import google.registry.dns.ReadDnsQueueAction;
|
||||
import google.registry.dns.ReadDnsRefreshRequestsAction;
|
||||
import google.registry.dns.RefreshDnsAction;
|
||||
import google.registry.dns.RefreshDnsOnHostRenameAction;
|
||||
@@ -143,8 +142,6 @@ interface BackendRequestComponent {
|
||||
|
||||
PublishSpec11ReportAction publishSpec11ReportAction();
|
||||
|
||||
ReadDnsQueueAction readDnsQueueAction();
|
||||
|
||||
ReadDnsRefreshRequestsAction readDnsRefreshRequestsAction();
|
||||
|
||||
RdeReportAction rdeReportAction();
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
// 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.persistence.converter;
|
||||
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/** JPA converter for {@link DatabaseMigrationStateSchedule} transitions. */
|
||||
@DeleteAfterMigration
|
||||
@Converter(autoApply = true)
|
||||
public class DatabaseMigrationScheduleTransitionConverter
|
||||
extends TimedTransitionPropertyConverterBase<MigrationState> {
|
||||
|
||||
@Override
|
||||
protected String convertValueToString(MigrationState value) {
|
||||
return value.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MigrationState convertStringToValue(String string) {
|
||||
return MigrationState.valueOf(string);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import static google.registry.request.RequestParameters.extractRequiredParameter
|
||||
import com.google.api.services.dataflow.Dataflow;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.Parameter;
|
||||
@@ -134,7 +134,7 @@ public class ReportingModule {
|
||||
/** Constructs a {@link Dataflow} API client with default settings. */
|
||||
@Provides
|
||||
static Dataflow provideDataflow(
|
||||
@DefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("projectId") String projectId) {
|
||||
return new Dataflow.Builder(
|
||||
credentialsBundle.getHttpTransport(),
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.monitoring.metrics.EventMetric;
|
||||
import com.google.monitoring.metrics.LabelDescriptor;
|
||||
import com.google.monitoring.metrics.MetricRegistryImpl;
|
||||
import google.registry.request.auth.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static google.registry.request.auth.AuthLevel.APP;
|
||||
import static google.registry.request.auth.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.request.auth.RequestAuthenticator.AuthMethod;
|
||||
import google.registry.request.auth.RequestAuthenticator.AuthSettings;
|
||||
import google.registry.request.auth.RequestAuthenticator.UserPolicy;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthMethod;
|
||||
import google.registry.request.auth.AuthSettings.UserPolicy;
|
||||
|
||||
/** Enum used to configure authentication settings for Actions. */
|
||||
public enum Auth {
|
||||
@@ -25,21 +25,18 @@ public enum Auth {
|
||||
/**
|
||||
* Allows anyone access, doesn't attempt to authenticate user.
|
||||
*
|
||||
* Will never return absent(), but only authenticates access from App Engine task-queues. For
|
||||
* <p>Will never return absent(), but only authenticates access from App Engine task-queues. For
|
||||
* everyone else - returns NOT_AUTHENTICATED.
|
||||
*/
|
||||
AUTH_PUBLIC_ANONYMOUS(
|
||||
ImmutableList.of(AuthMethod.INTERNAL),
|
||||
AuthLevel.NONE,
|
||||
UserPolicy.PUBLIC),
|
||||
AUTH_PUBLIC_ANONYMOUS(ImmutableList.of(AuthMethod.INTERNAL), AuthLevel.NONE, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows anyone access, does attempt to authenticate user.
|
||||
* Allows anyone to access, does attempt to authenticate user.
|
||||
*
|
||||
* If a user is logged in, will authenticate (and return) them. Otherwise, access is still
|
||||
* <p>If a user is logged in, will authenticate (and return) them. Otherwise, access is still
|
||||
* granted, but NOT_AUTHENTICATED is returned.
|
||||
*
|
||||
* Will never return absent().
|
||||
* <p>Will never return absent().
|
||||
*/
|
||||
AUTH_PUBLIC(
|
||||
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API, AuthMethod.LEGACY),
|
||||
@@ -47,17 +44,15 @@ public enum Auth {
|
||||
UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows anyone access, as long as they are logged in.
|
||||
* Allows anyone to access, as long as they are logged in.
|
||||
*
|
||||
* Does not allow access from App Engine task-queues.
|
||||
* <p>Does not allow access from App Engine task-queues.
|
||||
*/
|
||||
AUTH_PUBLIC_LOGGED_IN(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY),
|
||||
AuthLevel.USER,
|
||||
UserPolicy.PUBLIC),
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows anyone access, as long as they use OAuth to authenticate.
|
||||
* Allows anyone to access, as long as they use OAuth to authenticate.
|
||||
*
|
||||
* <p>Also allows access from App Engine task-queue. Note that OAuth client ID still needs to be
|
||||
* allow-listed in the config file for OAuth-based authentication to succeed.
|
||||
@@ -80,10 +75,7 @@ public enum Auth {
|
||||
|
||||
private final AuthSettings authSettings;
|
||||
|
||||
Auth(
|
||||
ImmutableList<AuthMethod> methods,
|
||||
AuthLevel minimumLevel,
|
||||
UserPolicy userPolicy) {
|
||||
Auth(ImmutableList<AuthMethod> methods, AuthLevel minimumLevel, UserPolicy userPolicy) {
|
||||
authSettings = AuthSettings.create(methods, minimumLevel, userPolicy);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
/**
|
||||
* Authentication level.
|
||||
*
|
||||
* <p>Used by {@link Auth} to specify what authentication is required, and by {@link AuthResult})
|
||||
* to specify what authentication was found. These are a series of levels, from least to most
|
||||
* authentication required. The lowest level of requirement, NONE, can be satisfied by any level
|
||||
* of authentication, while the highest level, USER, can only be satisfied by the authentication of
|
||||
* a specific user. The level returned may be higher than what was required, if more authentication
|
||||
* turns out to be possible. For instance, if an authenticated user is found, USER will be returned
|
||||
* even if no authentication was required.
|
||||
*/
|
||||
public enum AuthLevel {
|
||||
|
||||
/** No authentication was required/found. */
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Authentication required, but user not required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, but app-internal authentication (which isn't associated
|
||||
* with a specific user) is permitted.
|
||||
*
|
||||
* <p>In AuthResult: App-internal authentication was successful.
|
||||
*/
|
||||
APP,
|
||||
|
||||
/**
|
||||
* Authentication required, user required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, and app-internal authentication is forbidden, meaning
|
||||
* that a valid authentication result will contain specific user information.
|
||||
*
|
||||
* <p>In AuthResult: A valid user was authenticated.
|
||||
*/
|
||||
USER
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
|
||||
import com.google.appengine.api.oauth.OAuthService;
|
||||
import com.google.appengine.api.oauth.OAuthServiceFactory;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
@@ -21,35 +23,46 @@ import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.IapOidcAuthenticationMechanism;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.RegularOidcAuthenticationMechanism;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism.TokenExtractor;
|
||||
import javax.inject.Qualifier;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Dagger module for authentication routines.
|
||||
*/
|
||||
/** Dagger module for authentication routines. */
|
||||
@Module
|
||||
public class AuthModule {
|
||||
|
||||
// IAP-signed JWT will be in this header.
|
||||
// See https://cloud.google.com/iap/docs/signed-headers-howto#securing_iap_headers.
|
||||
public static final String IAP_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
|
||||
// GAE will put the content in header "proxy-authorization" in this header when it routes the
|
||||
// request to the app.
|
||||
public static final String PROXY_HEADER_NAME = "X-Google-Proxy-Authorization";
|
||||
public static final String BEARER_PREFIX = "Bearer ";
|
||||
// TODO: Change the IAP audience format once we are on GKE.
|
||||
// See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
|
||||
private static final String IAP_AUDIENCE_FORMAT = "/projects/%d/apps/%s";
|
||||
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
|
||||
private static final String SA_ISSUER_URL = "https://accounts.google.com";
|
||||
|
||||
/** Provides the custom authentication mechanisms (including OAuth). */
|
||||
/** Provides the custom authentication mechanisms (including OAuth and OIDC). */
|
||||
@Provides
|
||||
ImmutableList<AuthenticationMechanism> provideApiAuthenticationMechanisms(
|
||||
OAuthAuthenticationMechanism oauthAuthenticationMechanism,
|
||||
IapHeaderAuthenticationMechanism iapHeaderAuthenticationMechanism,
|
||||
ServiceAccountAuthenticationMechanism serviceAccountAuthenticationMechanism) {
|
||||
IapOidcAuthenticationMechanism iapOidcAuthenticationMechanism,
|
||||
RegularOidcAuthenticationMechanism regularOidcAuthenticationMechanism) {
|
||||
return ImmutableList.of(
|
||||
oauthAuthenticationMechanism,
|
||||
iapHeaderAuthenticationMechanism,
|
||||
serviceAccountAuthenticationMechanism);
|
||||
iapOidcAuthenticationMechanism,
|
||||
regularOidcAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@interface IAP {}
|
||||
@interface IapOidc {}
|
||||
|
||||
@Qualifier
|
||||
@interface ServiceAccount {}
|
||||
@interface RegularOidc {}
|
||||
|
||||
/** Provides the OAuthService instance. */
|
||||
@Provides
|
||||
@@ -58,18 +71,42 @@ public class AuthModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IAP
|
||||
@IapOidc
|
||||
@Singleton
|
||||
TokenVerifier provideTokenVerifier(
|
||||
TokenVerifier provideIapTokenVerifier(
|
||||
@Config("projectId") String projectId, @Config("projectIdNumber") long projectIdNumber) {
|
||||
String audience = String.format("/projects/%d/apps/%s", projectIdNumber, projectId);
|
||||
String audience = String.format(IAP_AUDIENCE_FORMAT, projectIdNumber, projectId);
|
||||
return TokenVerifier.newBuilder().setAudience(audience).setIssuer(IAP_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ServiceAccount
|
||||
@RegularOidc
|
||||
@Singleton
|
||||
TokenVerifier provideServiceAccountTokenVerifier(@Config("projectId") String projectId) {
|
||||
TokenVerifier provideRegularTokenVerifier(@Config("projectId") String projectId) {
|
||||
return TokenVerifier.newBuilder().setAudience(projectId).setIssuer(SA_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IapOidc
|
||||
@Singleton
|
||||
TokenExtractor provideIapTokenExtractor() {
|
||||
return request -> request.getHeader(IAP_HEADER_NAME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@RegularOidc
|
||||
@Singleton
|
||||
TokenExtractor provideRegularTokenExtractor() {
|
||||
return request -> {
|
||||
// TODO: only check the Authorizaiton header after the migration to OIDC is complete.
|
||||
String rawToken = request.getHeader(PROXY_HEADER_NAME);
|
||||
if (rawToken == null) {
|
||||
rawToken = request.getHeader(AUTHORIZATION);
|
||||
}
|
||||
if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) {
|
||||
return rawToken.substring(BEARER_PREFIX.length());
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.request.auth;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -66,6 +67,5 @@ public abstract class AuthResult {
|
||||
* returns NOT_AUTHENTICATED in this case, as opposed to absent() if authentication failed and was
|
||||
* required. So as a return from an authorization check, this can be treated as a success.
|
||||
*/
|
||||
public static final AuthResult NOT_AUTHENTICATED =
|
||||
AuthResult.create(AuthLevel.NONE);
|
||||
public static final AuthResult NOT_AUTHENTICATED = create(AuthLevel.NONE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
|
||||
/**
|
||||
* Parameters used to configure the authenticator.
|
||||
*
|
||||
* <p>AuthSettings shouldn't be used directly, instead - use one of the predefined {@link Auth} enum
|
||||
* values.
|
||||
*/
|
||||
@Immutable
|
||||
@AutoValue
|
||||
public abstract class AuthSettings {
|
||||
|
||||
public abstract ImmutableList<AuthMethod> methods();
|
||||
|
||||
public abstract AuthLevel minimumLevel();
|
||||
|
||||
public abstract UserPolicy userPolicy();
|
||||
|
||||
static AuthSettings create(
|
||||
ImmutableList<AuthMethod> methods, AuthLevel minimumLevel, UserPolicy userPolicy) {
|
||||
return new AutoValue_AuthSettings(methods, minimumLevel, userPolicy);
|
||||
}
|
||||
|
||||
/** Available methods for authentication. */
|
||||
public enum AuthMethod {
|
||||
|
||||
/** App Engine internal authentication. Must always be provided as the first method. */
|
||||
INTERNAL,
|
||||
|
||||
/** Authentication methods suitable for API-style access, such as OAuth 2. */
|
||||
API,
|
||||
|
||||
/** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */
|
||||
LEGACY
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication level.
|
||||
*
|
||||
* <p>Used by {@link Auth} to specify what authentication is required, and by {@link AuthResult})
|
||||
* to specify what authentication was found. These are a series of levels, from least to most
|
||||
* authentication required. The lowest level of requirement, NONE, can be satisfied by any level
|
||||
* of authentication, while the highest level, USER, can only be satisfied by the authentication
|
||||
* of a specific user. The level returned may be higher than what was required, if more
|
||||
* authentication turns out to be possible. For instance, if an authenticated user is found, USER
|
||||
* will be returned even if no authentication was required.
|
||||
*/
|
||||
public enum AuthLevel {
|
||||
|
||||
/** No authentication was required/found. */
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* Authentication required, but user not required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, but app-internal authentication (which isn't
|
||||
* associated with a specific user) is permitted.
|
||||
*
|
||||
* <p>In AuthResult: App-internal authentication was successful.
|
||||
*/
|
||||
APP,
|
||||
|
||||
/**
|
||||
* Authentication required, user required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, and app-internal authentication is forbidden, meaning
|
||||
* that a valid authentication result will contain specific user information.
|
||||
*
|
||||
* <p>In AuthResult: A valid user was authenticated.
|
||||
*/
|
||||
USER
|
||||
}
|
||||
|
||||
/** User authorization policy options. */
|
||||
public enum UserPolicy {
|
||||
|
||||
/** This action ignores end users; the only configured auth method must be INTERNAL. */
|
||||
IGNORED,
|
||||
|
||||
/** No user policy is enforced; anyone can access this action. */
|
||||
PUBLIC,
|
||||
|
||||
/**
|
||||
* If there is a user, it must be an admin, as determined by isUserAdmin().
|
||||
*
|
||||
* <p>Note that, according to App Engine, anybody with access to the app in the GCP Console,
|
||||
* including editors and viewers, is an admin.
|
||||
*/
|
||||
ADMIN
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.request.auth.AuthModule.IAP;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A way to authenticate HTTP requests that have gone through the GCP Identity-Aware Proxy.
|
||||
*
|
||||
* <p>When the user logs in, IAP provides a JWT in the <code>X-Goog-IAP-JWT-Assertion</code> header.
|
||||
* This header is included on all requests to IAP-enabled services (which should be all of them that
|
||||
* receive requests from the front end). The token verification libraries ensure that the signed
|
||||
* token has the proper audience and issuer.
|
||||
*
|
||||
* @see <a href="https://cloud.google.com/iap/docs/signed-headers-howto">the documentation on GCP
|
||||
* IAP's signed headers for more information.</a>
|
||||
*/
|
||||
public class IapHeaderAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private static final String ID_TOKEN_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
|
||||
|
||||
@Inject
|
||||
public IapHeaderAuthenticationMechanism(@IAP TokenVerifier tokenVerifier) {
|
||||
super(tokenVerifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
return request.getHeader(ID_TOKEN_HEADER_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
Optional<User> maybeUser = UserDao.loadUser(emailAddress);
|
||||
if (!maybeUser.isPresent()) {
|
||||
logger.atInfo().log("No user found for email address %s", emailAddress);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.console.User;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public abstract class IdTokenAuthenticationBase implements AuthenticationMechanism {
|
||||
|
||||
public static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the IAP-based authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static Optional<User> userForTesting = Optional.empty();
|
||||
|
||||
private final TokenVerifier tokenVerifier;
|
||||
|
||||
public IdTokenAuthenticationBase(TokenVerifier tokenVerifier) {
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
}
|
||||
|
||||
abstract String rawTokenFromRequest(HttpServletRequest request);
|
||||
|
||||
abstract AuthResult authResultFromEmail(String email);
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& userForTesting.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userForTesting.get()));
|
||||
}
|
||||
String rawIdToken = rawTokenFromRequest(request);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String emailAddress = (String) token.getPayload().get("email");
|
||||
return authResultFromEmail(emailAddress);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setUserAuthInfoForTestServer(@Nullable User user) {
|
||||
userForTesting = Optional.ofNullable(user);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static google.registry.request.auth.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthLevel.USER;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
import static google.registry.security.XsrfTokenManager.P_CSRF_TOKEN;
|
||||
import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static google.registry.request.auth.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthLevel.USER;
|
||||
import static google.registry.request.auth.AuthModule.BEARER_PREFIX;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
|
||||
import com.google.appengine.api.oauth.OAuthRequestException;
|
||||
import com.google.appengine.api.oauth.OAuthService;
|
||||
@@ -35,8 +36,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*/
|
||||
public class OAuthAuthenticationMechanism implements AuthenticationMechanism {
|
||||
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final OAuthService oauthService;
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.request.auth.AuthModule.IapOidc;
|
||||
import google.registry.request.auth.AuthModule.RegularOidc;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* An authenticam mechanism that verifies the OIDC token.
|
||||
*
|
||||
* <p>Currently, two flavors are supported: one that checkes for the OIDC token as a regular bearer
|
||||
* token, and another that checks for the OIDC token passed by IAP. In both cases, the {@link
|
||||
* AuthResult} with the highest {@link AuthLevel} possible is returned. So, if the email address for
|
||||
* which the token is minted exists both as a {@link User} and as a service account, the returned
|
||||
* {@link AuthResult} is at {@link AuthLevel#USER}.
|
||||
*
|
||||
* @see <a href="https://developers.google.com/identity/openid-connect/openid-connect">OpenID
|
||||
* Connect </a>
|
||||
*/
|
||||
public abstract class OidcTokenAuthenticationMechanism implements AuthenticationMechanism {
|
||||
|
||||
public static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
// A workaround that allows "use" of the OIDC authenticator when running local testing, i.e.
|
||||
// the RegistryTestServer
|
||||
private static AuthResult authResultForTesting = null;
|
||||
|
||||
protected final TokenVerifier tokenVerifier;
|
||||
|
||||
protected final TokenExtractor tokenExtractor;
|
||||
|
||||
private final ImmutableList<String> serviceAccountEmails;
|
||||
|
||||
protected OidcTokenAuthenticationMechanism(
|
||||
ImmutableList<String> serviceAccountEmails,
|
||||
TokenVerifier tokenVerifier,
|
||||
TokenExtractor tokenExtractor) {
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
this.tokenVerifier = tokenVerifier;
|
||||
this.tokenExtractor = tokenExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (RegistryEnvironment.get().equals(RegistryEnvironment.UNITTEST)
|
||||
&& authResultForTesting != null) {
|
||||
logger.atWarning().log("Using AuthResult %s for testing.", authResultForTesting);
|
||||
return authResultForTesting;
|
||||
}
|
||||
String rawIdToken = tokenExtractor.extract(request);
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
JsonWebSignature token;
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String email = (String) token.getPayload().get("email");
|
||||
if (email == null) {
|
||||
logger.atWarning().log("No email address from the OIDC token:\n%s", token.getPayload());
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
Optional<User> maybeUser = UserDao.loadUser(email);
|
||||
if (maybeUser.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
logger.atInfo().log("No end user found for email address %s", email);
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(email))) {
|
||||
return AuthResult.create(APP);
|
||||
}
|
||||
logger.atInfo().log("No service account found for email address %s", email);
|
||||
logger.atWarning().log(
|
||||
"The email address %s is not tied to a principal with access to Nomulus", email);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setAuthResultForTesting(@Nullable AuthResult authResult) {
|
||||
authResultForTesting = authResult;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void unsetAuthResultForTesting() {
|
||||
authResultForTesting = null;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface TokenExtractor {
|
||||
@Nullable
|
||||
String extract(HttpServletRequest request);
|
||||
}
|
||||
|
||||
/**
|
||||
* A mechanism to authenticate HTTP requests that have gone through the GCP Identity-Aware Proxy.
|
||||
*
|
||||
* <p>When the user logs in, IAP provides a JWT in the {@code X-Goog-IAP-JWT-Assertion} header.
|
||||
* This header is included on all requests to IAP-enabled services (which should be all of them
|
||||
* that receive requests from the front end). The token verification libraries ensure that the
|
||||
* signed token has the proper audience and issuer.
|
||||
*
|
||||
* @see <a href="https://cloud.google.com/iap/docs/signed-headers-howto">the documentation on GCP
|
||||
* IAP's signed headers for more information.</a>
|
||||
*/
|
||||
static class IapOidcAuthenticationMechanism extends OidcTokenAuthenticationMechanism {
|
||||
|
||||
@Inject
|
||||
protected IapOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@IapOidc TokenVerifier tokenVerifier,
|
||||
@IapOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mechanism to authenticate HTTP requests with an OIDC token as a bearer token.
|
||||
*
|
||||
* <p>If the endpoint is not behind IAP, we can try to authenticate the OIDC token supplied in the
|
||||
* request header directly. Ideally we would like all endpoints to be behind IAP, but being able
|
||||
* to authenticate the token directly provides us with the flexibility to do away with OAuth-based
|
||||
* {@link OAuthAuthenticationMechanism} that is tied to App Engine runtime without having to turn
|
||||
* on IAP, which is an all-or-nothing switch for each GAE service (i.e. no way to turn it on only
|
||||
* for certain GAE endpoints).
|
||||
*
|
||||
* <p>Note that this mechanism will try to first extract the token under the "proxy-authorization"
|
||||
* header, before trying "authorization". This is because currently the GAE OAuth service always
|
||||
* uses "authorization", and we would like to provide a way for both auth mechanisms to be working
|
||||
* at the same time for the same request.
|
||||
*
|
||||
* @see <a href=https://datatracker.ietf.org/doc/html/rfc6750>Bearer Token Usage</a>
|
||||
*/
|
||||
static class RegularOidcAuthenticationMechanism extends OidcTokenAuthenticationMechanism {
|
||||
|
||||
@Inject
|
||||
protected RegularOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@RegularOidc TokenVerifier tokenVerifier,
|
||||
@RegularOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,12 @@ package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthMethod;
|
||||
import google.registry.request.auth.AuthSettings.UserPolicy;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -44,57 +45,6 @@ public class RequestAuthenticator {
|
||||
this.legacyAuthenticationMechanism = legacyAuthenticationMechanism;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters used to configure the authenticator.
|
||||
*
|
||||
* AuthSettings shouldn't be used directly, instead - use one of the predefined {@link Auth} enum
|
||||
* values.
|
||||
*/
|
||||
@Immutable
|
||||
@AutoValue
|
||||
public abstract static class AuthSettings {
|
||||
|
||||
public abstract ImmutableList<AuthMethod> methods();
|
||||
public abstract AuthLevel minimumLevel();
|
||||
public abstract UserPolicy userPolicy();
|
||||
|
||||
static AuthSettings create(
|
||||
ImmutableList<AuthMethod> methods, AuthLevel minimumLevel, UserPolicy userPolicy) {
|
||||
return new AutoValue_RequestAuthenticator_AuthSettings(methods, minimumLevel, userPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
/** Available methods for authentication. */
|
||||
public enum AuthMethod {
|
||||
|
||||
/** App Engine internal authentication. Must always be provided as the first method. */
|
||||
INTERNAL,
|
||||
|
||||
/** Authentication methods suitable for API-style access, such as OAuth 2. */
|
||||
API,
|
||||
|
||||
/** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */
|
||||
LEGACY
|
||||
}
|
||||
|
||||
/** User authorization policy options. */
|
||||
public enum UserPolicy {
|
||||
|
||||
/** This action ignores end users; the only configured auth method must be INTERNAL. */
|
||||
IGNORED,
|
||||
|
||||
/** No user policy is enforced; anyone can access this action. */
|
||||
PUBLIC,
|
||||
|
||||
/**
|
||||
* If there is a user, it must be an admin, as determined by isUserAdmin().
|
||||
*
|
||||
* <p>Note that, according to App Engine, anybody with access to the app in the GCP Console,
|
||||
* including editors and viewers, is an admin.
|
||||
*/
|
||||
ADMIN
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to authenticate and authorize the user, according to the settings of the action.
|
||||
*
|
||||
@@ -169,7 +119,7 @@ public class RequestAuthenticator {
|
||||
return authResult;
|
||||
}
|
||||
break;
|
||||
// API-based user authentication mechanisms, such as OAuth
|
||||
// API-based user authentication mechanisms, such as OAuth
|
||||
case API:
|
||||
// checkAuthConfig will have insured that the user policy is not IGNORED.
|
||||
for (AuthenticationMechanism authMechanism : apiAuthenticationMechanisms) {
|
||||
@@ -181,7 +131,7 @@ public class RequestAuthenticator {
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Legacy authentication via UserService
|
||||
// Legacy authentication via UserService
|
||||
case LEGACY:
|
||||
// checkAuthConfig will have insured that the user policy is not IGNORED.
|
||||
authResult = legacyAuthenticationMechanism.authenticate(req);
|
||||
@@ -209,7 +159,7 @@ public class RequestAuthenticator {
|
||||
"Actions with INTERNAL auth method may not require USER auth level");
|
||||
checkArgument(
|
||||
!(auth.userPolicy().equals(UserPolicy.IGNORED)
|
||||
&& !authMethods.equals(ImmutableList.of(AuthMethod.INTERNAL))),
|
||||
&& !authMethods.equals(ImmutableList.of(AuthMethod.INTERNAL))),
|
||||
"Actions with auth methods beyond INTERNAL must not specify the IGNORED user policy");
|
||||
}
|
||||
}
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
// Copyright 2023 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static google.registry.request.auth.AuthLevel.APP;
|
||||
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.auth.AuthModule.ServiceAccount;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* A way to authenticate HTTP requests signed by Service Account
|
||||
*
|
||||
* <p>Currently used by cloud scheduler service account
|
||||
*/
|
||||
public class ServiceAccountAuthenticationMechanism extends IdTokenAuthenticationBase {
|
||||
|
||||
private static final String BEARER_PREFIX = "Bearer ";
|
||||
|
||||
private final ImmutableList<String> serviceAccountEmails;
|
||||
|
||||
@Inject
|
||||
public ServiceAccountAuthenticationMechanism(
|
||||
@ServiceAccount TokenVerifier tokenVerifier,
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails) {
|
||||
super(tokenVerifier);
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
}
|
||||
|
||||
@Override
|
||||
String rawTokenFromRequest(HttpServletRequest request) {
|
||||
String rawToken = request.getHeader(AUTHORIZATION);
|
||||
if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) {
|
||||
return rawToken.substring(BEARER_PREFIX.length());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
AuthResult authResultFromEmail(String emailAddress) {
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(emailAddress))) {
|
||||
return AuthResult.create(APP);
|
||||
} else {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import dagger.Lazy;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.CredentialModule.DefaultCredential;
|
||||
import google.registry.config.CredentialModule.LocalCredential;
|
||||
import google.registry.config.CredentialModule.LocalCredentialJson;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
@@ -222,10 +221,6 @@ public class AuthModule {
|
||||
|
||||
@Module
|
||||
abstract static class LocalCredentialModule {
|
||||
@Binds
|
||||
@DefaultCredential
|
||||
abstract GoogleCredentialsBundle provideLocalCredentialAsDefaultCredential(
|
||||
@LocalCredential GoogleCredentialsBundle credential);
|
||||
|
||||
@Binds
|
||||
@ApplicationDefaultCredential
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
// 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;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.common.TimedTransitionProperty;
|
||||
|
||||
/** A command to check the current Registry 3.0 migration state of the database. */
|
||||
@DeleteAfterMigration
|
||||
@Parameters(separators = " =", commandDescription = "Check current Registry 3.0 migration state")
|
||||
public class GetDatabaseMigrationStateCommand implements Command {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
TimedTransitionProperty<MigrationState> migrationSchedule =
|
||||
DatabaseMigrationStateSchedule.get();
|
||||
System.out.printf("Current migration schedule: %s%n", migrationSchedule.toValueMap());
|
||||
}
|
||||
}
|
||||
@@ -15,17 +15,23 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
import google.registry.util.DomainNameUtils;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Command to show a domain resource. */
|
||||
@Parameters(separators = " =", commandDescription = "Show domain resource(s)")
|
||||
final class GetDomainCommand extends GetEppResourceCommand {
|
||||
|
||||
@Parameter(names = "--show_deleted", description = "Include deleted domains in the print out")
|
||||
private boolean showDeleted = false;
|
||||
|
||||
@Parameter(
|
||||
description = "Fully qualified domain name(s)",
|
||||
required = true)
|
||||
@@ -35,10 +41,24 @@ final class GetDomainCommand extends GetEppResourceCommand {
|
||||
public void runAndPrint() {
|
||||
for (String domainName : mainParameters) {
|
||||
String canonicalDomain = DomainNameUtils.canonicalizeHostname(domainName);
|
||||
printResource(
|
||||
"Domain",
|
||||
canonicalDomain,
|
||||
loadByForeignKey(Domain.class, canonicalDomain, readTimestamp));
|
||||
if (showDeleted) {
|
||||
tm().transact(
|
||||
() ->
|
||||
tm()
|
||||
.createQueryComposer(Domain.class)
|
||||
.where("domainName", Comparator.EQ, canonicalDomain)
|
||||
.orderBy("creationTime")
|
||||
.stream()
|
||||
.forEach(
|
||||
d -> {
|
||||
printResource("Domain", canonicalDomain, Optional.of(d));
|
||||
}));
|
||||
} else {
|
||||
printResource(
|
||||
"Domain",
|
||||
canonicalDomain,
|
||||
loadByForeignKey(Domain.class, canonicalDomain, readTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ public final class RegistryTool {
|
||||
.put("get_allocation_token", GetAllocationTokenCommand.class)
|
||||
.put("get_claims_list", GetClaimsListCommand.class)
|
||||
.put("get_contact", GetContactCommand.class)
|
||||
.put("get_database_migration_state", GetDatabaseMigrationStateCommand.class)
|
||||
.put("get_domain", GetDomainCommand.class)
|
||||
.put("get_history_entries", GetHistoryEntriesCommand.class)
|
||||
.put("get_host", GetHostCommand.class)
|
||||
@@ -98,7 +97,6 @@ public final class RegistryTool {
|
||||
.put("renew_domain", RenewDomainCommand.class)
|
||||
.put("save_sql_credential", SaveSqlCredentialCommand.class)
|
||||
.put("send_escrow_report_to_icann", SendEscrowReportToIcannCommand.class)
|
||||
.put("set_database_migration_state", SetDatabaseMigrationStateCommand.class)
|
||||
.put("setup_ote", SetupOteCommand.class)
|
||||
.put("uniform_rapid_suspension", UniformRapidSuspensionCommand.class)
|
||||
.put("unlock_domain", UnlockDomainCommand.class)
|
||||
|
||||
@@ -48,8 +48,8 @@ class RequestFactoryModule {
|
||||
*
|
||||
* <p>If we need to have an IAP-enabled audience, we can use the existing refresh token and the
|
||||
* IAP client ID audience to request an IAP-enabled ID token. This token is read and used by
|
||||
* {@link google.registry.request.auth.IapHeaderAuthenticationMechanism}, and it requires that the
|
||||
* user have a {@link google.registry.model.console.User} object present in the database.
|
||||
* {@link IapHeaderAuthenticationMechanismMechanism}, and it requires that the user have a {@link
|
||||
* google.registry.model.console.User} object present in the database.
|
||||
*/
|
||||
private static final GenericUrl TOKEN_SERVER_URL =
|
||||
new GenericUrl(URI.create("https://oauth2.googleapis.com/token"));
|
||||
|
||||
@@ -85,7 +85,7 @@ public class ServiceConnection {
|
||||
private String internalSend(
|
||||
String endpoint, Map<String, ?> params, MediaType contentType, @Nullable byte[] payload)
|
||||
throws IOException {
|
||||
GenericUrl url = new GenericUrl(String.format("%s%s", getServer(), endpoint));
|
||||
GenericUrl url = new GenericUrl(String.format("%s%s", getServer(service), endpoint));
|
||||
url.putAll(params);
|
||||
HttpRequest request =
|
||||
(payload != null)
|
||||
@@ -141,7 +141,7 @@ public class ServiceConnection {
|
||||
return (Map<String, Object>) JSONValue.parse(response.substring(JSON_SAFETY_PREFIX.length()));
|
||||
}
|
||||
|
||||
public URL getServer() {
|
||||
public static URL getServer(Service service) {
|
||||
switch (service) {
|
||||
case DEFAULT:
|
||||
return RegistryConfig.getDefaultServer();
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// 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;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.tools.params.TransitionListParameter.MigrationStateTransitions;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command to set the Registry 3.0 database migration state schedule. */
|
||||
@DeleteAfterMigration
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription = "Set the current database migration state schedule.")
|
||||
public class SetDatabaseMigrationStateCommand extends ConfirmingCommand {
|
||||
|
||||
private static final String WARNING_MESSAGE =
|
||||
"Attempting to change the schedule with an effect that would take place within the next 10 "
|
||||
+ "minutes. The cache expiration duration is 5 minutes so this MAY BE DANGEROUS.\n";
|
||||
|
||||
@Parameter(
|
||||
names = "--migration_schedule",
|
||||
converter = MigrationStateTransitions.class,
|
||||
validateWith = MigrationStateTransitions.class,
|
||||
required = true,
|
||||
description =
|
||||
"Comma-delimited list of database transitions, of the form"
|
||||
+ " <time>=<migration-state>[,<time>=<migration-state>]*")
|
||||
ImmutableSortedMap<DateTime, MigrationState> transitionSchedule;
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return tm().transact(
|
||||
() -> {
|
||||
StringBuilder result = new StringBuilder();
|
||||
DateTime now = tm().getTransactionTime();
|
||||
DateTime nextTransition = transitionSchedule.ceilingKey(now);
|
||||
if (nextTransition != null && nextTransition.isBefore(now.plusMinutes(10))) {
|
||||
result.append(WARNING_MESSAGE);
|
||||
}
|
||||
return result
|
||||
.append(String.format("Set new migration state schedule %s?", transitionSchedule))
|
||||
.toString();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() {
|
||||
tm().transact(() -> DatabaseMigrationStateSchedule.set(transitionSchedule));
|
||||
return String.format("Successfully set new migration state schedule %s", transitionSchedule);
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ final class UpdateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
checkArgument(
|
||||
!domain.getStatusValues().contains(SERVER_UPDATE_PROHIBITED),
|
||||
"The domain '%s' has status SERVER_UPDATE_PROHIBITED. Verify that you are allowed "
|
||||
+ "to make updates, and if so, use the domain_unlock command to enable updates.",
|
||||
+ "to make updates, and if so, use the unlock_domain command to enable updates.",
|
||||
domainName);
|
||||
checkArgument(
|
||||
!domain.getStatusValues().contains(PENDING_DELETE) || forceInPendingDelete,
|
||||
|
||||
@@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import org.joda.money.Money;
|
||||
@@ -73,12 +72,4 @@ public abstract class TransitionListParameter<V> extends KeyValueMapParameter<Da
|
||||
return TokenStatus.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Converter-validator for states of the Registry 3.0 database migration. */
|
||||
public static class MigrationStateTransitions extends TransitionListParameter<MigrationState> {
|
||||
@Override
|
||||
protected MigrationState parseValue(String value) {
|
||||
return MigrationState.valueOf(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.model.tld.Registries.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
@@ -43,10 +43,10 @@ import org.joda.time.Duration;
|
||||
* run internally, or by pretending to be internal by setting the X-AppEngine-QueueName header,
|
||||
* which only admin users can do.
|
||||
*
|
||||
* <p>You must pass in a number of <code>smearMinutes</code> as a URL parameter so that the DNS
|
||||
* queue doesn't get overloaded. A rough rule of thumb for Cloud DNS is 1 minute per every 1,000
|
||||
* domains. This smears the updates out over the next N minutes. For small TLDs consisting of fewer
|
||||
* than 1,000 domains, passing in 1 is fine (which will execute all the updates immediately).
|
||||
* <p>You must pass in a number of {@code smearMinutes} as a URL parameter so that the DNS queue
|
||||
* doesn't get overloaded. A rough rule of thumb for Cloud DNS is 1 minute per every 1,000 domains.
|
||||
* This smears the updates out over the next N minutes. For small TLDs consisting of fewer than
|
||||
* 1,000 domains, passing in 1 is fine (which will execute all the updates immediately).
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.TOOLS,
|
||||
@@ -66,7 +66,6 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
@Parameter("smearMinutes")
|
||||
int smearMinutes;
|
||||
|
||||
@Inject DnsUtils dnsUtils;
|
||||
@Inject Clock clock;
|
||||
@Inject Random random;
|
||||
|
||||
@@ -91,7 +90,7 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
domainName -> {
|
||||
try {
|
||||
// Smear the task execution time over the next N minutes.
|
||||
dnsUtils.requestDomainDnsRefresh(
|
||||
requestDomainDnsRefresh(
|
||||
domainName, Duration.standardMinutes(random.nextInt(smearMinutes)));
|
||||
} catch (Throwable t) {
|
||||
logger.atSevere().withCause(t).log(
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
<class>google.registry.model.billing.BillingEvent</class>
|
||||
<class>google.registry.model.billing.BillingRecurrence</class>
|
||||
<class>google.registry.model.common.Cursor</class>
|
||||
<class>google.registry.model.common.DatabaseMigrationStateSchedule</class>
|
||||
<class>google.registry.model.common.DnsRefreshRequest</class>
|
||||
<class>google.registry.model.console.User</class>
|
||||
<class>google.registry.model.contact.ContactHistory</class>
|
||||
@@ -88,7 +87,6 @@
|
||||
<class>google.registry.persistence.converter.CommandNameSetConverter</class>
|
||||
<class>google.registry.persistence.converter.CurrencyToBillingConverter</class>
|
||||
<class>google.registry.persistence.converter.CurrencyUnitConverter</class>
|
||||
<class>google.registry.persistence.converter.DatabaseMigrationScheduleTransitionConverter</class>
|
||||
<class>google.registry.persistence.converter.DateTimeConverter</class>
|
||||
<class>google.registry.persistence.converter.DurationConverter</class>
|
||||
<class>google.registry.persistence.converter.IdnTableEnumSetConverter</class>
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.cloud.tasks.v2.OidcToken;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
@@ -46,9 +47,15 @@ public class CloudTasksUtilsTest {
|
||||
private final LinkedListMultimap<String, String> params = LinkedListMultimap.create();
|
||||
private final SerializableCloudTasksClient mockClient = mock(SerializableCloudTasksClient.class);
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2021-11-08"));
|
||||
private final CloudTasksUtils cloudTasksUtils =
|
||||
private CloudTasksUtils cloudTasksUtils =
|
||||
new CloudTasksUtils(
|
||||
new Retrier(new FakeSleeper(clock), 1), clock, "project", "location", mockClient);
|
||||
new Retrier(new FakeSleeper(clock), 1),
|
||||
clock,
|
||||
"project",
|
||||
"location",
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
mockClient);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
@@ -348,4 +355,255 @@ public class CloudTasksUtilsTest {
|
||||
verify(mockClient).enqueue("project", "location", "test-queue", task1);
|
||||
verify(mockClient).enqueue("project", "location", "test-queue", task2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks() {
|
||||
createOidcTasksUtils();
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks() {
|
||||
createOidcTasksUtils();
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withNullParams() {
|
||||
createOidcTasksUtils();
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withNullParams() {
|
||||
createOidcTasksUtils();
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withEmptyParams() {
|
||||
createOidcTasksUtils();
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withEmptyParams() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(100));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
Instant lowerBoundTime = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
Instant upperBound = Instant.ofEpochMilli(clock.nowUtc().plusSeconds(100).getMillis());
|
||||
|
||||
assertThat(scheduleTime.isBefore(lowerBoundTime)).isFalse();
|
||||
assertThat(upperBound.isBefore(scheduleTime)).isFalse();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(1));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isNotEqualTo(0);
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
Instant lowerBoundTime = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
Instant upperBound = Instant.ofEpochMilli(clock.nowUtc().plusSeconds(1).getMillis());
|
||||
|
||||
assertThat(scheduleTime.isBefore(lowerBoundTime)).isFalse();
|
||||
assertThat(upperBound.isBefore(scheduleTime)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withEmptyJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withEmptyJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withZeroJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withZeroJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withDelay() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withDelay() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isNotEqualTo(0);
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withZeroDelay() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withZeroDelay() {
|
||||
createOidcTasksUtils();
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay("/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private void createOidcTasksUtils() {
|
||||
cloudTasksUtils =
|
||||
new CloudTasksUtils(
|
||||
new Retrier(new FakeSleeper(clock), 1),
|
||||
clock,
|
||||
"project",
|
||||
"location",
|
||||
Optional.of("defaultServiceAccount"),
|
||||
Optional.of("iapClientId"),
|
||||
mockClient);
|
||||
}
|
||||
|
||||
private void verifyOidcToken(Task task) {
|
||||
assertThat(task.getHttpRequest().getOidcToken())
|
||||
.isEqualTo(
|
||||
OidcToken.newBuilder()
|
||||
.setServiceAccountEmail("defaultServiceAccount")
|
||||
.setAudience("iapClientId")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeLockHandler;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -59,8 +58,6 @@ class DeleteExpiredDomainsActionTest {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension final TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private DeleteExpiredDomainsAction action;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntitiesIfPresent;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
@@ -30,11 +31,9 @@ import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
@@ -47,7 +46,6 @@ import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.DnsUtilsHelper;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -72,9 +70,6 @@ class DeleteProberDataActionTest {
|
||||
|
||||
private DeleteProberDataAction action;
|
||||
|
||||
private final DnsUtils dnsUtils = mock(DnsUtils.class);
|
||||
private final DnsUtilsHelper dnsUtilsHelper = new DnsUtilsHelper(dnsUtils);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
// Entities in these two should not be touched.
|
||||
@@ -99,7 +94,6 @@ class DeleteProberDataActionTest {
|
||||
|
||||
private void resetAction() {
|
||||
action = new DeleteProberDataAction();
|
||||
action.dnsUtils = dnsUtils;
|
||||
action.isDryRun = false;
|
||||
action.tlds = ImmutableSet.of();
|
||||
action.registryAdminRegistrarId = "TheRegistrar";
|
||||
@@ -201,7 +195,7 @@ class DeleteProberDataActionTest {
|
||||
DateTime timeAfterDeletion = DateTime.now(UTC);
|
||||
assertThat(loadByForeignKey(Domain.class, "blah.ib-any.test", timeAfterDeletion)).isEmpty();
|
||||
assertThat(loadByEntity(domain).getDeletionTime()).isLessThan(timeAfterDeletion);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("blah.ib-any.test");
|
||||
assertDomainDnsRequests("blah.ib-any.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -218,7 +212,7 @@ class DeleteProberDataActionTest {
|
||||
action.run();
|
||||
assertThat(loadByForeignKey(Domain.class, "blah.ib-any.test", timeAfterDeletion)).isEmpty();
|
||||
assertThat(loadByEntity(domain).getDeletionTime()).isLessThan(timeAfterDeletion);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("blah.ib-any.test");
|
||||
assertDomainDnsRequests("blah.ib-any.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -83,7 +83,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.startDate()).isEqualTo("2017-10-01");
|
||||
assertThat(invoiceKey.endDate()).isEqualTo("2022-09-30");
|
||||
assertThat(invoiceKey.productAccountKey()).isEqualTo("12345-CRRHELLO");
|
||||
assertThat(invoiceKey.usageGroupingKey()).isEqualTo("myRegistrar - test");
|
||||
assertThat(invoiceKey.usageGroupingKey()).isEqualTo("myRegistrar");
|
||||
assertThat(invoiceKey.description()).isEqualTo("RENEW | TLD: test | TERM: 5-year");
|
||||
assertThat(invoiceKey.unitPrice()).isEqualTo(20.5);
|
||||
assertThat(invoiceKey.unitPriceCurrency()).isEqualTo("USD");
|
||||
@@ -104,7 +104,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.toCsv(3L))
|
||||
.isEqualTo(
|
||||
"2017-10-01,2022-09-30,12345-CRRHELLO,61.50,USD,10125,1,PURCHASE,"
|
||||
+ "myRegistrar - test,3,RENEW | TLD: test | TERM: 5-year,20.50,USD,");
|
||||
+ "myRegistrar,3,RENEW | TLD: test | TERM: 5-year,20.50,USD,");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,7 +114,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.toCsv(3L))
|
||||
.isEqualTo(
|
||||
"2017-10-01,,12345-CRRHELLO,61.50,USD,10125,1,PURCHASE,"
|
||||
+ "myRegistrar - test,3,RENEW | TLD: test | TERM: 0-year,20.50,USD,");
|
||||
+ "myRegistrar,3,RENEW | TLD: test | TERM: 0-year,20.50,USD,");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -224,13 +224,13 @@ class InvoicingPipelineTest {
|
||||
|
||||
private static final ImmutableList<String> EXPECTED_INVOICE_OUTPUT =
|
||||
ImmutableList.of(
|
||||
"2017-10-01,2020-09-30,234,41.00,USD,10125,1,PURCHASE,theRegistrar - test,2,"
|
||||
"2017-10-01,2020-09-30,234,41.00,USD,10125,1,PURCHASE,theRegistrar,2,"
|
||||
+ "RENEW | TLD: test | TERM: 3-year,20.50,USD,",
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,theRegistrar - hello,1,"
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,theRegistrar,1,"
|
||||
+ "CREATE | TLD: hello | TERM: 5-year,70.00,JPY,",
|
||||
"2017-10-01,,234,20.00,USD,10125,1,PURCHASE,theRegistrar - test,1,"
|
||||
"2017-10-01,,234,20.00,USD,10125,1,PURCHASE,theRegistrar,1,"
|
||||
+ "SERVER_STATUS | TLD: test | TERM: 0-year,20.00,USD,",
|
||||
"2017-10-01,2018-09-30,456,20.50,USD,10125,1,PURCHASE,bestdomains - test,1,"
|
||||
"2017-10-01,2018-09-30,456,20.50,USD,10125,1,PURCHASE,bestdomains,1,"
|
||||
+ "RENEW | TLD: test | TERM: 1-year,20.50,USD,116688");
|
||||
|
||||
private final InvoicingPipelineOptions options =
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.assertHostDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequestsExcept;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
|
||||
import static google.registry.testing.TaskQueueHelper.assertDnsTasksEnqueued;
|
||||
import static google.registry.testing.TaskQueueHelper.assertNoDnsTasksEnqueued;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -30,7 +31,6 @@ import google.registry.request.HttpException.NotFoundException;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.testing.CloudTasksHelper.CloudTasksHelperModule;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -43,18 +43,15 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
/** Unit tests for Dagger injection of the DNS package. */
|
||||
public final class DnsInjectionTest {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension final TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
private final HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
private final HttpServletResponse rsp = mock(HttpServletResponse.class);
|
||||
private final StringWriter httpOutput = new StringWriter();
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2014-01-01TZ"));
|
||||
private DnsTestComponent component;
|
||||
private DnsQueue dnsQueue;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
@@ -64,18 +61,18 @@ public final class DnsInjectionTest {
|
||||
.requestModule(new RequestModule(req, rsp))
|
||||
.cloudTasksHelperModule(new CloudTasksHelperModule(clock))
|
||||
.build();
|
||||
dnsQueue = component.dnsQueue();
|
||||
createTld("lol");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReadDnsQueueAction_injectsAndWorks() {
|
||||
void testReadDnsRefreshRequestsAction_injectsAndWorks() {
|
||||
persistActiveSubordinateHost("ns1.example.lol", persistActiveDomain("example.lol"));
|
||||
clock.advanceOneMilli();
|
||||
dnsQueue.addDomainRefreshTask("example.lol");
|
||||
DnsUtils.requestDomainDnsRefresh("example.lol");
|
||||
when(req.getParameter("tld")).thenReturn("lol");
|
||||
component.readDnsQueueAction().run();
|
||||
assertNoDnsTasksEnqueued();
|
||||
clock.advanceOneMilli();
|
||||
component.readDnsRefreshRequestsAction().run();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,7 +81,7 @@ public final class DnsInjectionTest {
|
||||
when(req.getParameter("type")).thenReturn("domain");
|
||||
when(req.getParameter("name")).thenReturn("example.lol");
|
||||
component.refreshDns().run();
|
||||
assertDnsTasksEnqueued("example.lol");
|
||||
assertNoDnsRequestsExcept("example.lol");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -102,7 +99,7 @@ public final class DnsInjectionTest {
|
||||
when(req.getParameter("type")).thenReturn("host");
|
||||
when(req.getParameter("name")).thenReturn("ns1.example.lol");
|
||||
component.refreshDns().run();
|
||||
assertDnsTasksEnqueued("ns1.example.lol");
|
||||
assertHostDnsRequests("ns1.example.lol");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.TaskQueueHelper.assertNoTasksEnqueued;
|
||||
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link DnsQueue}. */
|
||||
public class DnsQueueTest {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension final TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
private DnsQueue dnsQueue;
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2010-01-01T10:00:00Z"));
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
dnsQueue = DnsQueue.createForTesting(clock);
|
||||
dnsQueue.leaseTasksBatchSize = 10;
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_addHostRefreshTask_success() {
|
||||
createTld("tld");
|
||||
dnsQueue.addHostRefreshTask("octopus.tld");
|
||||
assertTasksEnqueued(
|
||||
"dns-pull",
|
||||
new TaskMatcher()
|
||||
.param("Target-Type", "HOST")
|
||||
.param("Target-Name", "octopus.tld")
|
||||
.param("Create-Time", "2010-01-01T10:00:00.000Z")
|
||||
.param("tld", "tld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_addHostRefreshTask_failsOnUnknownTld() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
try {
|
||||
dnsQueue.addHostRefreshTask("octopus.notatld");
|
||||
} finally {
|
||||
assertNoTasksEnqueued("dns-pull");
|
||||
}
|
||||
});
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("octopus.notatld is not a subordinate host to a known tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_addDomainRefreshTask_success() {
|
||||
createTld("tld");
|
||||
dnsQueue.addDomainRefreshTask("octopus.tld");
|
||||
assertTasksEnqueued(
|
||||
"dns-pull",
|
||||
new TaskMatcher()
|
||||
.param("Target-Type", "DOMAIN")
|
||||
.param("Target-Name", "octopus.tld")
|
||||
.param("Create-Time", "2010-01-01T10:00:00.000Z")
|
||||
.param("tld", "tld"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_addDomainRefreshTask_failsOnUnknownTld() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
try {
|
||||
dnsQueue.addDomainRefreshTask("fake.notatld");
|
||||
} finally {
|
||||
assertNoTasksEnqueued("dns-pull");
|
||||
}
|
||||
});
|
||||
assertThat(thrown).hasMessageThat().contains("TLD notatld does not exist");
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ import javax.inject.Singleton;
|
||||
VoidDnsWriterModule.class,
|
||||
})
|
||||
interface DnsTestComponent {
|
||||
DnsQueue dnsQueue();
|
||||
RefreshDnsAction refreshDns();
|
||||
ReadDnsQueueAction readDnsQueueAction();
|
||||
|
||||
ReadDnsRefreshRequestsAction readDnsRefreshRequestsAction();
|
||||
}
|
||||
|
||||
@@ -20,20 +20,10 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule;
|
||||
import google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
@@ -42,7 +32,6 @@ import java.util.Comparator;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -54,52 +43,22 @@ public class DnsUtilsTest {
|
||||
private static final String domainName = "test.tld";
|
||||
private static final String hostName = "ns1.test.tld";
|
||||
|
||||
private final DnsQueue dnsQueue = mock(DnsQueue.class);
|
||||
private final DnsUtils dnsUtils = new DnsUtils(dnsQueue);
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2020-02-02T01:23:45Z"));
|
||||
|
||||
@RegisterExtension
|
||||
JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
DatabaseMigrationStateSchedule.useUncachedForTest();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld(tld);
|
||||
when(dnsQueue.getClock()).thenReturn(clock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_hostRefresh_pullQueue() {
|
||||
dnsUtils.requestHostDnsRefresh(hostName);
|
||||
verify(dnsQueue).addHostRefreshTask(hostName);
|
||||
assertThat(loadAllOf(DnsRefreshRequest.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_domainRefresh_pullQueue() {
|
||||
dnsUtils.requestDomainDnsRefresh(domainName);
|
||||
verify(dnsQueue).addDomainRefreshTask(domainName, Duration.ZERO);
|
||||
assertThat(loadAllOf(DnsRefreshRequest.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_domainRefreshWithDelay_pullQueue() {
|
||||
dnsUtils.requestDomainDnsRefresh(domainName, Duration.standardMinutes(3));
|
||||
verify(dnsQueue).addDomainRefreshTask(domainName, Duration.standardMinutes(3));
|
||||
assertThat(loadAllOf(DnsRefreshRequest.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_hostRefresh_unmanagedHost() {
|
||||
String unmanagedHostName = "ns1.another.example";
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class, () -> dnsUtils.requestHostDnsRefresh(unmanagedHostName));
|
||||
verify(dnsQueue, never()).addHostRefreshTask(anyString());
|
||||
IllegalArgumentException.class, () -> DnsUtils.requestHostDnsRefresh(unmanagedHostName));
|
||||
assertThat(loadAllOf(DnsRefreshRequest.class)).isEmpty();
|
||||
}
|
||||
|
||||
@@ -108,34 +67,27 @@ public class DnsUtilsTest {
|
||||
String unmanagedDomainName = "another.example";
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> dnsUtils.requestDomainDnsRefresh(unmanagedDomainName));
|
||||
verify(dnsQueue, never()).addDomainRefreshTask(anyString(), any(Duration.class));
|
||||
() -> DnsUtils.requestDomainDnsRefresh(unmanagedDomainName));
|
||||
assertThat(loadAllOf(DnsRefreshRequest.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_hostRefresh() {
|
||||
useDnsSql();
|
||||
dnsUtils.requestHostDnsRefresh(hostName);
|
||||
verify(dnsQueue, never()).addHostRefreshTask(anyString());
|
||||
DnsUtils.requestHostDnsRefresh(hostName);
|
||||
DnsRefreshRequest request = Iterables.getOnlyElement(loadAllOf(DnsRefreshRequest.class));
|
||||
assertRequest(request, TargetType.HOST, hostName, tld, clock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_domainRefresh() {
|
||||
useDnsSql();
|
||||
dnsUtils.requestDomainDnsRefresh(domainName);
|
||||
verify(dnsQueue, never()).addDomainRefreshTask(anyString(), any(Duration.class));
|
||||
DnsUtils.requestDomainDnsRefresh(domainName);
|
||||
DnsRefreshRequest request = Iterables.getOnlyElement(loadAllOf(DnsRefreshRequest.class));
|
||||
assertRequest(request, TargetType.DOMAIN, domainName, tld, clock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_domainRefreshWithDelay() {
|
||||
useDnsSql();
|
||||
dnsUtils.requestDomainDnsRefresh(domainName, Duration.standardMinutes(3));
|
||||
verify(dnsQueue, never()).addDomainRefreshTask(anyString(), any(Duration.class));
|
||||
DnsUtils.requestDomainDnsRefresh(domainName, Duration.standardMinutes(3));
|
||||
DnsRefreshRequest request = Iterables.getOnlyElement(loadAllOf(DnsRefreshRequest.class));
|
||||
assertRequest(request, TargetType.DOMAIN, domainName, tld, clock.nowUtc().plusMinutes(3));
|
||||
}
|
||||
@@ -182,7 +134,7 @@ public class DnsUtilsTest {
|
||||
|
||||
// Requests within cooldown period not included.
|
||||
requests =
|
||||
dnsUtils.readAndUpdateRequestsWithLatestProcessTime("tld", Duration.standardMinutes(1), 4);
|
||||
DnsUtils.readAndUpdateRequestsWithLatestProcessTime("tld", Duration.standardMinutes(1), 4);
|
||||
assertThat(requests.size()).isEqualTo(1);
|
||||
assertRequest(
|
||||
requests.get(0),
|
||||
@@ -195,7 +147,7 @@ public class DnsUtilsTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_deleteRequests() {
|
||||
dnsUtils.deleteRequests(processRequests());
|
||||
DnsUtils.deleteRequests(processRequests());
|
||||
ImmutableList<DnsRefreshRequest> remainingRequests =
|
||||
loadAllOf(DnsRefreshRequest.class).stream()
|
||||
.sorted(Comparator.comparing(DnsRefreshRequest::getRequestTime))
|
||||
@@ -222,31 +174,30 @@ public class DnsUtilsTest {
|
||||
tm().transact(() -> tm().delete(remainingRequests.get(2)));
|
||||
assertThat(loadAllOf(DnsRefreshRequest.class).size()).isEqualTo(2);
|
||||
// Should not throw even though one of the request is already deleted.
|
||||
dnsUtils.deleteRequests(remainingRequests);
|
||||
DnsUtils.deleteRequests(remainingRequests);
|
||||
assertThat(loadAllOf(DnsRefreshRequest.class).size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private ImmutableList<DnsRefreshRequest> processRequests() {
|
||||
useDnsSql();
|
||||
createTld("example");
|
||||
// Domain Included.
|
||||
dnsUtils.requestDomainDnsRefresh("test1.tld", Duration.standardMinutes(1));
|
||||
DnsUtils.requestDomainDnsRefresh("test1.tld", Duration.standardMinutes(1));
|
||||
// This one should be returned before test1.tld, even though it's added later, because of
|
||||
// the delay specified in test1.tld.
|
||||
dnsUtils.requestDomainDnsRefresh("test2.tld");
|
||||
DnsUtils.requestDomainDnsRefresh("test2.tld");
|
||||
// Not included because the TLD is not under management.
|
||||
dnsUtils.requestDomainDnsRefresh("something.example", Duration.standardMinutes(2));
|
||||
DnsUtils.requestDomainDnsRefresh("something.example", Duration.standardMinutes(2));
|
||||
clock.advanceBy(Duration.standardMinutes(3));
|
||||
// Host included.
|
||||
dnsUtils.requestHostDnsRefresh("ns1.test2.tld");
|
||||
DnsUtils.requestHostDnsRefresh("ns1.test2.tld");
|
||||
// Not included because the request time is in the future
|
||||
dnsUtils.requestDomainDnsRefresh("test4.tld", Duration.standardMinutes(2));
|
||||
DnsUtils.requestDomainDnsRefresh("test4.tld", Duration.standardMinutes(2));
|
||||
// Included after the previous one. Same request time, order by insertion order (i.e. ID);
|
||||
dnsUtils.requestDomainDnsRefresh("test5.tld");
|
||||
DnsUtils.requestDomainDnsRefresh("test5.tld");
|
||||
// Not included because batch size is exceeded;
|
||||
dnsUtils.requestDomainDnsRefresh("test6.tld");
|
||||
DnsUtils.requestDomainDnsRefresh("test6.tld");
|
||||
clock.advanceBy(Duration.standardMinutes(1));
|
||||
return dnsUtils.readAndUpdateRequestsWithLatestProcessTime(
|
||||
return DnsUtils.readAndUpdateRequestsWithLatestProcessTime(
|
||||
"tld", Duration.standardMinutes(1), 4);
|
||||
}
|
||||
|
||||
@@ -268,26 +219,4 @@ public class DnsUtilsTest {
|
||||
assertThat(request.getRequestTime()).isEqualTo(requestTime);
|
||||
assertThat(request.getLastProcessTime()).isEqualTo(processTime);
|
||||
}
|
||||
|
||||
private void useDnsSql() {
|
||||
DateTime currentTime = clock.nowUtc();
|
||||
clock.setTo(START_OF_TIME);
|
||||
tm().transact(
|
||||
() ->
|
||||
DatabaseMigrationStateSchedule.set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(5), MigrationState.SQL_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(6), MigrationState.SQL_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(7), MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
|
||||
.put(START_OF_TIME.plusMillis(8), MigrationState.NORDN_SQL)
|
||||
.put(START_OF_TIME.plusMillis(9), MigrationState.DNS_SQL)
|
||||
.build()));
|
||||
clock.setTo(currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsModule.PARAM_DNS_WRITER;
|
||||
import static google.registry.dns.DnsModule.PARAM_DOMAINS;
|
||||
import static google.registry.dns.DnsModule.PARAM_HOSTS;
|
||||
@@ -23,8 +22,13 @@ import static google.registry.dns.DnsModule.PARAM_LOCK_INDEX;
|
||||
import static google.registry.dns.DnsModule.PARAM_NUM_PUBLISH_LOCKS;
|
||||
import static google.registry.dns.DnsModule.PARAM_PUBLISH_TASK_ENQUEUED;
|
||||
import static google.registry.dns.DnsModule.PARAM_REFRESH_REQUEST_TIME;
|
||||
import static google.registry.dns.DnsUtils.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.PublishDnsUpdatesAction.RETRIES_BEFORE_PERMANENT_FAILURE;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertHostDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequestsExcept;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
|
||||
@@ -54,7 +58,6 @@ import google.registry.request.HttpException.ServiceUnavailableException;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DnsUtilsHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeLockHandler;
|
||||
import google.registry.testing.FakeResponse;
|
||||
@@ -83,8 +86,6 @@ public class PublishDnsUpdatesActionTest {
|
||||
private final FakeLockHandler lockHandler = new FakeLockHandler(true);
|
||||
private final DnsWriter dnsWriter = mock(DnsWriter.class);
|
||||
private final DnsMetrics dnsMetrics = mock(DnsMetrics.class);
|
||||
private final DnsUtils dnsUtils = mock(DnsUtils.class);
|
||||
private final DnsUtilsHelper dnsUtilsHelper = new DnsUtilsHelper(dnsUtils);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private PublishDnsUpdatesAction action;
|
||||
private InternetAddress outgoingRegistry;
|
||||
@@ -95,6 +96,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
createTld("xn--q9jyb4c");
|
||||
createTld("com");
|
||||
outgoingRegistry = new InternetAddress("outgoing@registry.example");
|
||||
registrySupportEmail = Lazies.of(new InternetAddress("registry@test.com"));
|
||||
registryCcEmail = Lazies.of(new InternetAddress("registry-cc@test.com"));
|
||||
@@ -161,7 +163,6 @@ public class PublishDnsUpdatesActionTest {
|
||||
outgoingRegistry,
|
||||
Optional.ofNullable(retryCount),
|
||||
Optional.empty(),
|
||||
dnsUtils,
|
||||
new DnsWriterProxy(ImmutableMap.of("correctWriter", dnsWriter)),
|
||||
dnsMetrics,
|
||||
lockHandler,
|
||||
@@ -195,7 +196,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
}
|
||||
|
||||
@@ -222,7 +223,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
}
|
||||
|
||||
@@ -275,7 +276,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -495,7 +496,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -525,7 +526,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -553,7 +554,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -579,9 +580,9 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.com");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns1.example.com");
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertDomainDnsRequests("example.com");
|
||||
assertHostDnsRequests("ns1.example.com");
|
||||
assertNoDnsRequestsExcept("example.com", "ns1.example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -607,9 +608,9 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.com");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns1.example.com");
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertDomainDnsRequests("example.com");
|
||||
assertHostDnsRequests("ns1.example.com");
|
||||
assertNoDnsRequestsExcept("example.com", "ns1.example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -631,11 +632,12 @@ public class PublishDnsUpdatesActionTest {
|
||||
Duration.standardHours(2),
|
||||
Duration.standardHours(1));
|
||||
verifyNoMoreInteractions(dnsMetrics);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.com");
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example2.com");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns1.example.com");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns2.example.com");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns1.example2.com");
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertDomainDnsRequests("example.com");
|
||||
assertDomainDnsRequests("example2.com");
|
||||
assertHostDnsRequests("ns1.example.com");
|
||||
assertHostDnsRequests("ns2.example.com");
|
||||
assertHostDnsRequests("ns1.example2.com");
|
||||
assertNoDnsRequestsExcept(
|
||||
"example.com", "example2.com", "ns1.example.com", "ns2.example.com", "ns1.example2.com");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,500 +0,0 @@
|
||||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.Lists.transform;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsConstants.DNS_PULL_QUEUE_NAME;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_CREATE_TIME_PARAM;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
|
||||
import static google.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.appengine.api.taskqueue.QueueFactory;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import google.registry.testing.TaskQueueHelper;
|
||||
import google.registry.testing.UriParameters;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junitpioneer.jupiter.RetryingTest;
|
||||
|
||||
/** Unit tests for {@link ReadDnsQueueAction}. */
|
||||
public class ReadDnsQueueActionTest {
|
||||
|
||||
private static final int TEST_TLD_UPDATE_BATCH_SIZE = 100;
|
||||
private DnsQueue dnsQueue;
|
||||
// Because of a bug in the queue test environment - b/73372999 - we must set the fake date of the
|
||||
// test in the future. Set to year 3000 so it'll remain in the future for a very long time.
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("3000-01-01TZ"));
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension final TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
// Because of b/73372999 - the FakeClock can't be in the past, or the TaskQueues stop working.
|
||||
// To make sure it's never in the past, we set the date far-far into the future
|
||||
clock.setTo(DateTime.parse("3000-01-01TZ"));
|
||||
createTlds("com", "net", "example", "multilock.uk");
|
||||
persistResource(Tld.get("com").asBuilder().setDnsWriters(ImmutableSet.of("comWriter")).build());
|
||||
persistResource(Tld.get("net").asBuilder().setDnsWriters(ImmutableSet.of("netWriter")).build());
|
||||
persistResource(
|
||||
Tld.get("example")
|
||||
.asBuilder()
|
||||
.setTldType(TldType.TEST)
|
||||
.setDnsWriters(ImmutableSet.of("exampleWriter"))
|
||||
.build());
|
||||
persistResource(
|
||||
Tld.get("multilock.uk")
|
||||
.asBuilder()
|
||||
.setNumDnsPublishLocks(1000)
|
||||
.setDnsWriters(ImmutableSet.of("multilockWriter"))
|
||||
.build());
|
||||
dnsQueue = DnsQueue.createForTesting(clock);
|
||||
}
|
||||
|
||||
private void run() {
|
||||
ReadDnsQueueAction action =
|
||||
new ReadDnsQueueAction(
|
||||
TEST_TLD_UPDATE_BATCH_SIZE,
|
||||
Duration.standardSeconds(10),
|
||||
Optional.empty(),
|
||||
clock,
|
||||
dnsQueue,
|
||||
Hashing.murmur3_32(),
|
||||
cloudTasksHelper.getTestCloudTasksUtils());
|
||||
// Advance the time a little, to ensure that leaseTasks() returns all tasks.
|
||||
clock.advanceBy(Duration.standardHours(1));
|
||||
action.run();
|
||||
}
|
||||
|
||||
private static TaskOptions createRefreshTask(String name, TargetType type) {
|
||||
TaskOptions options =
|
||||
TaskOptions.Builder.withMethod(TaskOptions.Method.PULL)
|
||||
.param(DNS_TARGET_TYPE_PARAM, type.toString())
|
||||
.param(DNS_TARGET_NAME_PARAM, name)
|
||||
.param(DNS_TARGET_CREATE_TIME_PARAM, "3000-01-01TZ");
|
||||
String tld = InternetDomainName.from(name).parts().reverse().get(0);
|
||||
return options.param("tld", tld);
|
||||
}
|
||||
|
||||
private static TaskQueueHelper.TaskMatcher createDomainRefreshTaskMatcher(String name) {
|
||||
return new TaskQueueHelper.TaskMatcher()
|
||||
.param(DNS_TARGET_NAME_PARAM, name)
|
||||
.param(DNS_TARGET_TYPE_PARAM, TargetType.DOMAIN.toString());
|
||||
}
|
||||
|
||||
private void assertTldsEnqueuedInPushQueue(ImmutableMultimap<String, String> tldsToDnsWriters) {
|
||||
// By default, the publishDnsUpdates tasks will be enqueued one hour after the update items were
|
||||
// created in the pull queue. This is because of the clock.advanceBy in run()
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
transform(
|
||||
tldsToDnsWriters.entries().asList(),
|
||||
(Entry<String, String> tldToDnsWriter) ->
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("tld", tldToDnsWriter.getKey())
|
||||
.param("dnsWriter", tldToDnsWriter.getValue())
|
||||
.param("requestTime", "3000-01-01T00:00:00.000Z")
|
||||
.param("enqueued", "3000-01-01T01:00:00.000Z")
|
||||
// Single-lock TLDs should use lock 1 of 1 by default
|
||||
.param("lockIndex", "1")
|
||||
.param("numPublishLocks", "1")
|
||||
.header("content-type", "application/x-www-form-urlencoded")));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_methodPostIsDefault() {
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.net");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher().method(HttpMethod.POST),
|
||||
new TaskMatcher().method(HttpMethod.POST),
|
||||
new TaskMatcher().method(HttpMethod.POST));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_allSingleLockTlds() {
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.net");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "net", "netWriter", "example", "exampleWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_moreUpdatesThanQueueBatchSize() {
|
||||
// The task queue has a batch size of 1000 (that's the maximum number of items you can lease at
|
||||
// once).
|
||||
ImmutableList<String> domains =
|
||||
IntStream.range(0, 1500)
|
||||
.mapToObj(i -> String.format("domain_%04d.com", i))
|
||||
.collect(toImmutableList());
|
||||
domains.forEach(dnsQueue::addDomainRefreshTask);
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
ImmutableList<Task> queuedTasks =
|
||||
ImmutableList.copyOf(cloudTasksHelper.getTestTasksFor(DNS_PUBLISH_PUSH_QUEUE_NAME));
|
||||
// ReadDnsQueueAction batches items per TLD in batches of size 100.
|
||||
// So for 1500 items in the DNS queue, we expect 15 items in the push queue
|
||||
assertThat(queuedTasks).hasSize(15);
|
||||
// Check all the expected domains are indeed enqueued
|
||||
assertThat(
|
||||
queuedTasks.stream()
|
||||
.flatMap(
|
||||
task ->
|
||||
UriParameters.parse(
|
||||
task.getAppEngineHttpRequest()
|
||||
.getBody()
|
||||
.toString(StandardCharsets.UTF_8))
|
||||
.get("domains")
|
||||
.stream())
|
||||
.flatMap(values -> Splitter.on(',').splitToStream(values)))
|
||||
.containsExactlyElementsIn(domains);
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_twoDnsWriters() {
|
||||
persistResource(
|
||||
Tld.get("com")
|
||||
.asBuilder()
|
||||
.setDnsWriters(ImmutableSet.of("comWriter", "otherWriter"))
|
||||
.build());
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
assertTldsEnqueuedInPushQueue(ImmutableMultimap.of("com", "comWriter", "com", "otherWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_differentUpdateTimes_usesMinimum() {
|
||||
clock.setTo(DateTime.parse("3000-02-03TZ"));
|
||||
dnsQueue.addDomainRefreshTask("domain1.com");
|
||||
clock.setTo(DateTime.parse("3000-02-04TZ"));
|
||||
dnsQueue.addDomainRefreshTask("domain2.com");
|
||||
clock.setTo(DateTime.parse("3000-02-05TZ"));
|
||||
dnsQueue.addDomainRefreshTask("domain3.com");
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.param("enqueued", "3000-02-05T01:00:00.000Z")
|
||||
.param("requestTime", "3000-02-03T00:00:00.000Z")
|
||||
.param("tld", "com")
|
||||
.param("dnsWriter", "comWriter")
|
||||
.param("domains", "domain1.com,domain2.com,domain3.com")
|
||||
.param("hosts", "")
|
||||
.param("lockIndex", "1")
|
||||
.param("numPublishLocks", "1"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_oneTldPaused_returnedToQueue() {
|
||||
persistResource(Tld.get("net").asBuilder().setDnsPaused(true).build());
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.net");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertTasksEnqueued(
|
||||
DNS_PULL_QUEUE_NAME, createDomainRefreshTaskMatcher("domain.net"));
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "example", "exampleWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_oneTldUnknown_returnedToQueue() {
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
QueueFactory.getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(
|
||||
TaskOptions.Builder.withDefaults()
|
||||
.method(TaskOptions.Method.PULL)
|
||||
.param(DNS_TARGET_TYPE_PARAM, TargetType.DOMAIN.toString())
|
||||
.param(DNS_TARGET_NAME_PARAM, "domain.unknown")
|
||||
.param(DNS_TARGET_CREATE_TIME_PARAM, "3000-01-01TZ")
|
||||
.param(PARAM_TLD, "unknown"));
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertTasksEnqueued(
|
||||
DNS_PULL_QUEUE_NAME, createDomainRefreshTaskMatcher("domain.unknown"));
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "example", "exampleWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_corruptTaskTldMismatch_published() {
|
||||
// TODO(mcilwain): what's the correct action to take in this case?
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
QueueFactory.getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(
|
||||
TaskOptions.Builder.withDefaults()
|
||||
.method(TaskOptions.Method.PULL)
|
||||
.param(DNS_TARGET_TYPE_PARAM, TargetType.DOMAIN.toString())
|
||||
.param(DNS_TARGET_NAME_PARAM, "domain.wrongtld")
|
||||
.param(DNS_TARGET_CREATE_TIME_PARAM, "3000-01-01TZ")
|
||||
.param(PARAM_TLD, "net"));
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "example", "exampleWriter", "net", "netWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_corruptTaskNoTld_discarded() {
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
QueueFactory.getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(
|
||||
TaskOptions.Builder.withDefaults()
|
||||
.method(TaskOptions.Method.PULL)
|
||||
.param(DNS_TARGET_TYPE_PARAM, TargetType.DOMAIN.toString())
|
||||
.param(DNS_TARGET_NAME_PARAM, "domain.net"));
|
||||
|
||||
run();
|
||||
|
||||
// The corrupt task isn't in the pull queue, but also isn't in the push queue
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "example", "exampleWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_corruptTaskNoName_discarded() {
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
QueueFactory.getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(
|
||||
TaskOptions.Builder.withDefaults()
|
||||
.method(TaskOptions.Method.PULL)
|
||||
.param(DNS_TARGET_TYPE_PARAM, TargetType.DOMAIN.toString())
|
||||
.param(PARAM_TLD, "net"));
|
||||
|
||||
run();
|
||||
|
||||
// The corrupt task isn't in the pull queue, but also isn't in the push queue
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "example", "exampleWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_corruptTaskNoType_discarded() {
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
QueueFactory.getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(
|
||||
TaskOptions.Builder.withDefaults()
|
||||
.method(TaskOptions.Method.PULL)
|
||||
.param(DNS_TARGET_NAME_PARAM, "domain.net")
|
||||
.param(PARAM_TLD, "net"));
|
||||
|
||||
run();
|
||||
|
||||
// The corrupt task isn't in the pull queue, but also isn't in the push queue
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "example", "exampleWriter"));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_corruptTaskWrongType_discarded() {
|
||||
dnsQueue.addDomainRefreshTask("domain.com");
|
||||
dnsQueue.addDomainRefreshTask("domain.example");
|
||||
QueueFactory.getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(
|
||||
TaskOptions.Builder.withDefaults()
|
||||
.method(TaskOptions.Method.PULL)
|
||||
.param(DNS_TARGET_TYPE_PARAM, "Wrong type")
|
||||
.param(DNS_TARGET_NAME_PARAM, "domain.net")
|
||||
.param(PARAM_TLD, "net"));
|
||||
|
||||
run();
|
||||
|
||||
// The corrupt task isn't in the pull queue, but also isn't in the push queue
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
assertTldsEnqueuedInPushQueue(
|
||||
ImmutableMultimap.of("com", "comWriter", "example", "exampleWriter"));
|
||||
}
|
||||
|
||||
private static String makeCommaSeparatedRange(int from, int to, String format) {
|
||||
return IntStream.range(from, to)
|
||||
.mapToObj(i -> String.format(format, i))
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_manyDomainsAndHosts() {
|
||||
for (int i = 0; i < 150; i++) {
|
||||
// 0: domain; 1: host 1; 2: host 2
|
||||
for (int thingType = 0; thingType < 3; thingType++) {
|
||||
for (String tld : ImmutableList.of("com", "net")) {
|
||||
String domainName = String.format("domain%04d.%s", i, tld);
|
||||
switch (thingType) {
|
||||
case 1:
|
||||
getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(createRefreshTask("ns1." + domainName, TargetType.HOST));
|
||||
break;
|
||||
case 2:
|
||||
getQueue(DNS_PULL_QUEUE_NAME)
|
||||
.add(createRefreshTask("ns2." + domainName, TargetType.HOST));
|
||||
break;
|
||||
default:
|
||||
dnsQueue.addDomainRefreshTask(domainName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", makeCommaSeparatedRange(0, 100, "domain%04d.com"))
|
||||
.param("hosts", ""),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", makeCommaSeparatedRange(100, 150, "domain%04d.com"))
|
||||
.param("hosts", makeCommaSeparatedRange(0, 50, "ns1.domain%04d.com")),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", "")
|
||||
.param("hosts", makeCommaSeparatedRange(50, 150, "ns1.domain%04d.com")),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", "")
|
||||
.param("hosts", makeCommaSeparatedRange(0, 100, "ns2.domain%04d.com")),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", "")
|
||||
.param("hosts", makeCommaSeparatedRange(100, 150, "ns2.domain%04d.com")),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", makeCommaSeparatedRange(0, 100, "domain%04d.net"))
|
||||
.param("hosts", ""),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", makeCommaSeparatedRange(100, 150, "domain%04d.net"))
|
||||
.param("hosts", makeCommaSeparatedRange(0, 50, "ns1.domain%04d.net")),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", "")
|
||||
.param("hosts", makeCommaSeparatedRange(50, 150, "ns1.domain%04d.net")),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", "")
|
||||
.param("hosts", makeCommaSeparatedRange(0, 100, "ns2.domain%04d.net")),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("domains", "")
|
||||
.param("hosts", makeCommaSeparatedRange(100, 150, "ns2.domain%04d.net")));
|
||||
}
|
||||
|
||||
@RetryingTest(4)
|
||||
void testSuccess_lockGroupsHostBySuperordinateDomain() {
|
||||
dnsQueue.addDomainRefreshTask("hello.multilock.uk");
|
||||
dnsQueue.addHostRefreshTask("ns1.abc.hello.multilock.uk");
|
||||
dnsQueue.addHostRefreshTask("ns2.hello.multilock.uk");
|
||||
dnsQueue.addDomainRefreshTask("another.multilock.uk");
|
||||
dnsQueue.addHostRefreshTask("ns3.def.another.multilock.uk");
|
||||
dnsQueue.addHostRefreshTask("ns4.another.multilock.uk");
|
||||
|
||||
run();
|
||||
|
||||
TaskQueueHelper.assertNoTasksEnqueued(DNS_PULL_QUEUE_NAME);
|
||||
// Expect two different groups; in-balliwick hosts are locked with their superordinate domains.
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("tld", "multilock.uk")
|
||||
.param("dnsWriter", "multilockWriter")
|
||||
.param("requestTime", "3000-01-01T00:00:00.000Z")
|
||||
.param("enqueued", "3000-01-01T01:00:00.000Z")
|
||||
.param("domains", "hello.multilock.uk")
|
||||
.param("hosts", "ns1.abc.hello.multilock.uk,ns2.hello.multilock.uk")
|
||||
.header("content-type", "application/x-www-form-urlencoded"),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.param("tld", "multilock.uk")
|
||||
.param("dnsWriter", "multilockWriter")
|
||||
.param("requestTime", "3000-01-01T00:00:00.000Z")
|
||||
.param("enqueued", "3000-01-01T01:00:00.000Z")
|
||||
.param("domains", "another.multilock.uk")
|
||||
.param("hosts", "ns3.def.another.multilock.uk,ns4.another.multilock.uk")
|
||||
.header("content-type", "application/x-www-form-urlencoded"));
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,6 @@
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.MigrationState;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.set;
|
||||
import static google.registry.model.common.DatabaseMigrationStateSchedule.useUncachedForTest;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -38,9 +34,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.model.common.DnsRefreshRequestTest;
|
||||
import google.registry.model.tld.Tld;
|
||||
@@ -54,7 +48,6 @@ import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -65,7 +58,6 @@ public class ReadDnsRefreshRequestsActionTest {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2020-02-02T01:23:45Z"));
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper(clock);
|
||||
private final DnsUtils dnsUtils = new DnsUtils(null);
|
||||
private final Optional<Integer> jitterSeconds = Optional.of(5);
|
||||
|
||||
@RegisterExtension
|
||||
@@ -80,20 +72,13 @@ public class ReadDnsRefreshRequestsActionTest {
|
||||
jitterSeconds,
|
||||
"tld",
|
||||
clock,
|
||||
dnsUtils,
|
||||
null,
|
||||
cloudTasksHelper.getTestCloudTasksUtils()));
|
||||
|
||||
private ImmutableList<DnsRefreshRequest> requests;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
useUncachedForTest();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
useDnsSql();
|
||||
persistResource(
|
||||
createTld("tld")
|
||||
.asBuilder()
|
||||
@@ -271,26 +256,4 @@ public class ReadDnsRefreshRequestsActionTest {
|
||||
.isAtMost(Duration.standardSeconds(jitterSeconds.get()));
|
||||
});
|
||||
}
|
||||
|
||||
private void useDnsSql() {
|
||||
DateTime currentTime = clock.nowUtc();
|
||||
clock.setTo(START_OF_TIME);
|
||||
tm().transact(
|
||||
() ->
|
||||
set(
|
||||
new ImmutableSortedMap.Builder<DateTime, MigrationState>(Ordering.natural())
|
||||
.put(START_OF_TIME, MigrationState.DATASTORE_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(1), MigrationState.DATASTORE_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(2), MigrationState.DATASTORE_PRIMARY_NO_ASYNC)
|
||||
.put(
|
||||
START_OF_TIME.plusMillis(3), MigrationState.DATASTORE_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(4), MigrationState.SQL_PRIMARY_READ_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(5), MigrationState.SQL_PRIMARY)
|
||||
.put(START_OF_TIME.plusMillis(6), MigrationState.SQL_ONLY)
|
||||
.put(START_OF_TIME.plusMillis(7), MigrationState.SEQUENCE_BASED_ALLOCATE_ID)
|
||||
.put(START_OF_TIME.plusMillis(8), MigrationState.NORDN_SQL)
|
||||
.put(START_OF_TIME.plusMillis(9), MigrationState.DNS_SQL)
|
||||
.build()));
|
||||
clock.setTo(currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,22 @@
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertHostDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequestsExcept;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveSubordinateHost;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import google.registry.dns.DnsConstants.TargetType;
|
||||
import google.registry.dns.DnsUtils.TargetType;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.NotFoundException;
|
||||
import google.registry.testing.DnsUtilsHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -41,12 +43,10 @@ public class RefreshDnsActionTest {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private final DnsUtils dnsUtils = mock(DnsUtils.class);
|
||||
private final DnsUtilsHelper dnsUtilsHelper = new DnsUtilsHelper(dnsUtils);
|
||||
private final FakeClock clock = new FakeClock();
|
||||
|
||||
private void run(TargetType type, String name) {
|
||||
new RefreshDnsAction(name, type, clock, dnsUtils).run();
|
||||
new RefreshDnsAction(name, type, clock).run();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -58,9 +58,9 @@ public class RefreshDnsActionTest {
|
||||
void testSuccess_host() {
|
||||
Domain domain = persistActiveDomain("example.xn--q9jyb4c");
|
||||
persistActiveSubordinateHost("ns1.example.xn--q9jyb4c", domain);
|
||||
run(TargetType.HOST, "ns1.example.xn--q9jyb4c");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns1.example.xn--q9jyb4c");
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
run(DnsUtils.TargetType.HOST, "ns1.example.xn--q9jyb4c");
|
||||
assertHostDnsRequests("ns1.example.xn--q9jyb4c");
|
||||
assertNoDnsRequestsExcept("ns1.example.xn--q9jyb4c");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -72,9 +72,9 @@ public class RefreshDnsActionTest {
|
||||
BadRequestException.class,
|
||||
() -> {
|
||||
try {
|
||||
run(TargetType.HOST, "ns1.example.xn--q9jyb4c");
|
||||
run(DnsUtils.TargetType.HOST, "ns1.example.xn--q9jyb4c");
|
||||
} finally {
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
});
|
||||
assertThat(thrown)
|
||||
@@ -85,23 +85,25 @@ public class RefreshDnsActionTest {
|
||||
@Test
|
||||
void testSuccess_domain() {
|
||||
persistActiveDomain("example.xn--q9jyb4c");
|
||||
run(TargetType.DOMAIN, "example.xn--q9jyb4c");
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.xn--q9jyb4c");
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
run(DnsUtils.TargetType.DOMAIN, "example.xn--q9jyb4c");
|
||||
assertDomainDnsRequests("example.xn--q9jyb4c");
|
||||
assertNoDnsRequestsExcept("example.xn--q9jyb4c");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_unqualifiedName() {
|
||||
assertThrows(BadRequestException.class, () -> run(TargetType.DOMAIN, "example"));
|
||||
assertThrows(BadRequestException.class, () -> run(DnsUtils.TargetType.DOMAIN, "example"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_hostDoesNotExist() {
|
||||
assertThrows(NotFoundException.class, () -> run(TargetType.HOST, "ns1.example.xn--q9jyb4c"));
|
||||
assertThrows(
|
||||
NotFoundException.class, () -> run(DnsUtils.TargetType.HOST, "ns1.example.xn--q9jyb4c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_domainDoesNotExist() {
|
||||
assertThrows(NotFoundException.class, () -> run(TargetType.DOMAIN, "example.xn--q9jyb4c"));
|
||||
assertThrows(
|
||||
NotFoundException.class, () -> run(DnsUtils.TargetType.DOMAIN, "example.xn--q9jyb4c"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package google.registry.dns;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
@@ -23,14 +25,12 @@ import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DnsUtilsHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -42,8 +42,6 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
public class RefreshDnsOnHostRenameActionTest {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2015-01-15T11:22:33Z"));
|
||||
private final DnsUtils dnsUtils = mock(DnsUtils.class);
|
||||
private final DnsUtilsHelper dnsUtilsHelper = new DnsUtilsHelper(dnsUtils);
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
|
||||
@RegisterExtension
|
||||
@@ -53,7 +51,7 @@ public class RefreshDnsOnHostRenameActionTest {
|
||||
private RefreshDnsOnHostRenameAction action;
|
||||
|
||||
private void createAction(String hostKey) {
|
||||
action = new RefreshDnsOnHostRenameAction(hostKey, response, dnsUtils);
|
||||
action = new RefreshDnsOnHostRenameAction(hostKey, response);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -75,7 +73,7 @@ public class RefreshDnsOnHostRenameActionTest {
|
||||
persistDomainAsDeleted(newDomain("deleted.tld", host), clock.nowUtc().minusDays(1));
|
||||
createAction(host.createVKey().stringify());
|
||||
action.run();
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld", "otherexample.tld");
|
||||
assertDomainDnsRequests("example.tld", "otherexample.tld");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
}
|
||||
|
||||
@@ -83,7 +81,7 @@ public class RefreshDnsOnHostRenameActionTest {
|
||||
void testFailure_nonexistentHost() {
|
||||
createAction("kind:Host@sql:rO0ABXQABGJsYWg");
|
||||
action.run();
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_NO_CONTENT);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Host to refresh does not exist: VKey<Host>(sql:blah)");
|
||||
@@ -95,7 +93,7 @@ public class RefreshDnsOnHostRenameActionTest {
|
||||
persistResource(newDomain("example.tld", host));
|
||||
createAction(host.createVKey().stringify());
|
||||
action.run();
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_NO_CONTENT);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Host to refresh is already deleted: ns1.example.tld");
|
||||
|
||||
@@ -47,7 +47,6 @@ import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import google.registry.tmch.TmchData;
|
||||
import google.registry.tmch.TmchTestData;
|
||||
import org.joda.money.Money;
|
||||
@@ -72,8 +71,6 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension final TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTlds("example", "tld");
|
||||
|
||||
@@ -27,7 +27,6 @@ import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -39,8 +38,6 @@ class EppLifecycleHostTest extends EppTestCase {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension final TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
@Test
|
||||
void testLifecycle() throws Exception {
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
|
||||
@@ -36,7 +36,6 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
|
||||
import google.registry.testing.EppLoader;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeHttpSession;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -51,8 +50,6 @@ class EppPointInTimeTest {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
@RegisterExtension final TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
private EppLoader eppLoader;
|
||||
|
||||
@BeforeEach
|
||||
|
||||
@@ -23,8 +23,6 @@ import google.registry.batch.AsyncTaskEnqueuerTest;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule.TmchCaMode;
|
||||
import google.registry.dns.DnsQueue;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.flows.custom.CustomLogicFactory;
|
||||
import google.registry.flows.custom.TestCustomLogicFactory;
|
||||
import google.registry.flows.domain.DomainFlowTmchUtils;
|
||||
@@ -32,7 +30,6 @@ import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.request.RequestScope;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.DnsUtilsHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeLockHandler;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
@@ -54,23 +51,17 @@ public interface EppTestComponent {
|
||||
class FakesAndMocksModule {
|
||||
|
||||
private AsyncTaskEnqueuer asyncTaskEnqueuer;
|
||||
private DnsQueue dnsQueue;
|
||||
private DomainFlowTmchUtils domainFlowTmchUtils;
|
||||
private EppMetric.Builder metricBuilder;
|
||||
private FakeClock clock;
|
||||
private FakeLockHandler lockHandler;
|
||||
private Sleeper sleeper;
|
||||
private CloudTasksHelper cloudTasksHelper;
|
||||
private DnsUtilsHelper dnsUtilsHelper;
|
||||
|
||||
public CloudTasksHelper getCloudTasksHelper() {
|
||||
return cloudTasksHelper;
|
||||
}
|
||||
|
||||
public DnsUtilsHelper getDnsUtilsHelper() {
|
||||
return dnsUtilsHelper;
|
||||
}
|
||||
|
||||
public EppMetric.Builder getMetricBuilder() {
|
||||
return metricBuilder;
|
||||
}
|
||||
@@ -85,11 +76,9 @@ public interface EppTestComponent {
|
||||
new DomainFlowTmchUtils(
|
||||
new TmchXmlSignature(new TmchCertificateAuthority(TmchCaMode.PILOT, clock)));
|
||||
instance.sleeper = new FakeSleeper(instance.clock);
|
||||
instance.dnsQueue = DnsQueue.createForTesting(clock);
|
||||
instance.metricBuilder = EppMetric.builderForRequest(clock);
|
||||
instance.lockHandler = new FakeLockHandler(true);
|
||||
instance.cloudTasksHelper = cloudTasksHelper;
|
||||
instance.dnsUtilsHelper = new DnsUtilsHelper();
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -103,11 +92,6 @@ public interface EppTestComponent {
|
||||
return cloudTasksHelper.getTestCloudTasksUtils();
|
||||
}
|
||||
|
||||
@Provides
|
||||
DnsUtils provideDnsUtils() {
|
||||
return dnsUtilsHelper.getDnsUtils();
|
||||
}
|
||||
|
||||
@Provides
|
||||
Clock provideClock() {
|
||||
return clock;
|
||||
@@ -123,11 +107,6 @@ public interface EppTestComponent {
|
||||
return new TestCustomLogicFactory();
|
||||
}
|
||||
|
||||
@Provides
|
||||
DnsQueue provideDnsQueue() {
|
||||
return dnsQueue;
|
||||
}
|
||||
|
||||
@Provides
|
||||
DomainFlowTmchUtils provideDomainFlowTmchUtils() {
|
||||
return domainFlowTmchUtils;
|
||||
|
||||
@@ -45,7 +45,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.DnsUtilsHelper;
|
||||
import google.registry.testing.EppLoader;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeHttpSession;
|
||||
@@ -85,7 +84,6 @@ public abstract class FlowTestCase<F extends Flow> {
|
||||
protected TransportCredentials credentials = new PasswordOnlyTransportCredentials();
|
||||
protected EppRequestSource eppRequestSource = EppRequestSource.UNIT_TEST;
|
||||
protected CloudTasksHelper cloudTasksHelper;
|
||||
protected DnsUtilsHelper dnsUtilsHelper;
|
||||
|
||||
private EppMetric.Builder eppMetricBuilder;
|
||||
|
||||
@@ -217,7 +215,6 @@ public abstract class FlowTestCase<F extends Flow> {
|
||||
|
||||
FakesAndMocksModule fakesAndMocksModule = FakesAndMocksModule.create(clock);
|
||||
cloudTasksHelper = fakesAndMocksModule.getCloudTasksHelper();
|
||||
dnsUtilsHelper = fakesAndMocksModule.getDnsUtilsHelper();
|
||||
// Run the flow.
|
||||
return DaggerEppTestComponent.builder()
|
||||
.fakesAndMocksModule(fakesAndMocksModule)
|
||||
|
||||
@@ -14,12 +14,10 @@
|
||||
|
||||
package google.registry.flows;
|
||||
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.PARAM_RESOURCE_KEY;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.LogsSubject.assertAboutLogs;
|
||||
import static google.registry.testing.TaskQueueHelper.assertTasksEnqueued;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -30,7 +28,6 @@ import google.registry.model.contact.ContactBase;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
import google.registry.model.domain.DomainBase;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.eppinput.EppInput.ResourceCommandWrapper;
|
||||
import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.host.HostBase;
|
||||
@@ -39,14 +36,13 @@ import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.tmch.ClaimsList;
|
||||
import google.registry.model.tmch.ClaimsListDao;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.TaskQueueHelper.TaskMatcher;
|
||||
import google.registry.testing.TestCacheExtension;
|
||||
import google.registry.util.JdkLoggerConfig;
|
||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||
import java.time.Duration;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.json.simple.JSONValue;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -64,11 +60,11 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
|
||||
|
||||
@RegisterExtension
|
||||
public final TestCacheExtension testCacheExtension =
|
||||
new TestCacheExtension.Builder().withClaimsListCache(java.time.Duration.ofHours(6)).build();
|
||||
new TestCacheExtension.Builder().withClaimsListCache(Duration.ofHours(6)).build();
|
||||
|
||||
@BeforeEach
|
||||
void beforeResourceFlowTestCase() {
|
||||
// Attach TestLogHandler to the root logger so it has access to all log messages.
|
||||
// Attach TestLogHandler to the root logger, so it has access to all log messages.
|
||||
// Note that in theory for assertIcannReportingActivityFieldLogged() below it would suffice to
|
||||
// attach it only to the FlowRunner logger, but for some reason this doesn't work for all flows.
|
||||
JdkLoggerConfig.getConfig("").addHandler(logHandler);
|
||||
@@ -110,22 +106,6 @@ public abstract class ResourceFlowTestCase<F extends Flow, R extends EppResource
|
||||
ClaimsListDao.save(ClaimsList.create(clock.nowUtc(), labelsToKeys));
|
||||
}
|
||||
|
||||
/** Asserts the presence of a single enqueued async contact or host deletion */
|
||||
protected <T extends EppResource> void assertAsyncDeletionTaskEnqueued(
|
||||
T resource, String requestingClientId, Trid trid, boolean isSuperuser) {
|
||||
TaskMatcher expected =
|
||||
new TaskMatcher()
|
||||
.etaDelta(Duration.standardSeconds(75), Duration.standardSeconds(105)) // expected: 90
|
||||
.param(PARAM_RESOURCE_KEY, resource.createVKey().stringify())
|
||||
.param("requestingClientId", requestingClientId)
|
||||
.param("serverTransactionId", trid.getServerTransactionId())
|
||||
.param("isSuperuser", Boolean.toString(isSuperuser))
|
||||
.param("requestedTime", clock.nowUtc().toString());
|
||||
trid.getClientTransactionId()
|
||||
.ifPresent(clTrid -> expected.param("clientTransactionId", clTrid));
|
||||
assertTasksEnqueued("async-delete-pull", expected);
|
||||
}
|
||||
|
||||
protected void assertClientIdFieldLogged(String registrarId) {
|
||||
assertAboutLogs()
|
||||
.that(logHandler)
|
||||
|
||||
@@ -39,6 +39,8 @@ import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertPollMessagesForResource;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
@@ -176,7 +178,6 @@ import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.TaskQueueExtension;
|
||||
import google.registry.tmch.LordnTaskUtils.LordnPhase;
|
||||
import google.registry.tmch.SmdrlCsvParser;
|
||||
import google.registry.tmch.TmchData;
|
||||
@@ -190,15 +191,12 @@ import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junitpioneer.jupiter.cartesian.CartesianTest;
|
||||
import org.junitpioneer.jupiter.cartesian.CartesianTest.Values;
|
||||
|
||||
/** Unit tests for {@link DomainCreateFlow}. */
|
||||
class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain> {
|
||||
|
||||
@RegisterExtension TaskQueueExtension taskQueue = new TaskQueueExtension();
|
||||
|
||||
private static final String CLAIMS_KEY = "2013041500/2/6/9/rJ1NrDO92vDsAzf7EQzgjX4R0000000001";
|
||||
// This is a time when SMD itself is not valid, but its signing certificates are. It will
|
||||
// trigger a different exception than when any signing cert is not valid yet.
|
||||
@@ -378,7 +376,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
GracePeriod.create(
|
||||
GracePeriodStatus.ADD, domain.getRepoId(), billingTime, "TheRegistrar", null),
|
||||
createBillingEvent));
|
||||
dnsUtilsHelper.assertDomainDnsRequests(getUniqueIdFromCommand());
|
||||
assertDomainDnsRequests(getUniqueIdFromCommand());
|
||||
}
|
||||
|
||||
private void assertNoLordn() throws Exception {
|
||||
@@ -901,7 +899,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
persistContactsAndHosts("net");
|
||||
runFlowAssertResponse(loadFile("domain_create_response_idn_minna.xml"));
|
||||
assertSuccessfulCreate("xn--q9jyb4c", ImmutableSet.of());
|
||||
dnsUtilsHelper.assertDomainDnsRequests("xn--abc-873b2e7eb1k8a4lpjvv.xn--q9jyb4c");
|
||||
assertDomainDnsRequests("xn--abc-873b2e7eb1k8a4lpjvv.xn--q9jyb4c");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -910,7 +908,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -923,7 +921,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutDomains()
|
||||
.that(reloadResourceByForeignKey())
|
||||
.hasRegistrationExpirationTime(clock.nowUtc().plusYears(1));
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -941,7 +939,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(loadFile("domain_create_response_claims.xml"));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of());
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example-one.tld");
|
||||
assertDomainDnsRequests("example-one.tld");
|
||||
assertClaimsLordn();
|
||||
}
|
||||
|
||||
@@ -970,7 +968,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(loadFile("domain_create_response_claims.xml"));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(RESERVED), allocationToken);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example-one.tld");
|
||||
assertDomainDnsRequests("example-one.tld");
|
||||
assertClaimsLordn();
|
||||
assertAllocationTokenWasRedeemed("abcDEF23456");
|
||||
}
|
||||
@@ -983,7 +981,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of());
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1383,7 +1381,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(loadFile("domain_create_response_claims.xml"));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT), allocationToken);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example-one.tld");
|
||||
assertDomainDnsRequests("example-one.tld");
|
||||
assertClaimsLordn();
|
||||
assertAllocationTokenWasRedeemed("abcDEF23456");
|
||||
}
|
||||
@@ -1463,7 +1461,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"EXPIRATION_TIME",
|
||||
SMD_VALID_TIME.plusYears(2).toString())));
|
||||
assertSuccessfulCreate("tld", ImmutableSet.of(ANCHOR_TENANT, SUNRISE), allocationToken);
|
||||
dnsUtilsHelper.assertDomainDnsRequests("test-validate.tld");
|
||||
assertDomainDnsRequests("test-validate.tld");
|
||||
assertSunriseLordn();
|
||||
assertAllocationTokenWasRedeemed("abcDEF23456");
|
||||
}
|
||||
@@ -2123,7 +2121,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertSunriseLordn();
|
||||
|
||||
// Check for SERVER_HOLD status, no DNS tasks enqueued, and collision poll message.
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
assertThat(domain.getStatusValues()).contains(SERVER_HOLD);
|
||||
assertPollMessagesWithCollisionOneTime(domain);
|
||||
@@ -2140,7 +2138,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "badcrash.tld")));
|
||||
|
||||
// Check for SERVER_HOLD status, no DNS tasks enqueued, and collision poll message.
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
Domain domain = reloadResourceByForeignKey();
|
||||
assertThat(domain.getStatusValues()).contains(SERVER_HOLD);
|
||||
assertPollMessagesWithCollisionOneTime(domain);
|
||||
|
||||
@@ -34,6 +34,7 @@ import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_DELETE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_TRANSFER_REQUEST;
|
||||
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertPollMessages;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
@@ -357,7 +358,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
// The add grace period is for a billable action, so it should trigger a cancellation.
|
||||
assertAutorenewClosedAndCancellationCreatedFor(
|
||||
graceBillingEvent, getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE, DomainHistory.class));
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
// There should be no poll messages. The previous autorenew poll message should now be deleted.
|
||||
assertThat(getPollMessages("TheRegistrar")).isEmpty();
|
||||
}
|
||||
@@ -744,7 +745,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
.build());
|
||||
DateTime eventTime = clock.nowUtc();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
assertAutorenewClosedAndCancellationCreatedFor(
|
||||
graceBillingEvent,
|
||||
getOnlyHistoryEntryOfType(domain, DOMAIN_DELETE, DomainHistory.class),
|
||||
@@ -762,7 +763,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(loadFile("domain_delete_response_pending.xml"));
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
assertOnlyBillingEventIsClosedAutorenew("TheRegistrar");
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.flows.domain;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertPollMessages;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
@@ -188,7 +189,7 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
.and()
|
||||
.hasLastEppUpdateRegistrarId("TheRegistrar");
|
||||
assertThat(domain.getGracePeriods()).isEmpty();
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
// The poll message for the delete should now be gone. The only poll message should be the new
|
||||
// autorenew poll message.
|
||||
assertPollMessages(
|
||||
@@ -256,7 +257,7 @@ class DomainRestoreRequestFlowTest extends ResourceFlowTestCase<DomainRestoreReq
|
||||
.and()
|
||||
.hasLastEppUpdateRegistrarId("TheRegistrar");
|
||||
assertThat(domain.getGracePeriods()).isEmpty();
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
// The poll message for the delete should now be gone. The only poll message should be the new
|
||||
// autorenew poll message.
|
||||
assertPollMessages(
|
||||
|
||||
@@ -22,15 +22,21 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_DELETE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_HOLD;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_RENEW_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_TRANSFER_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_UPDATE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.PENDING_DELETE;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_DELETE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_HOLD;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_RENEW_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_TRANSFER_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.SERVER_UPDATE_PROHIBITED;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
import static google.registry.model.tld.Tld.TldState.QUIET_PERIOD;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertPollMessagesForResource;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
@@ -96,7 +102,6 @@ import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.secdns.DomainDsData;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppcommon.Trid;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
@@ -204,7 +209,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
// Check that the domain was updated. These values came from the xml.
|
||||
assertAboutDomains()
|
||||
.that(domain)
|
||||
.hasStatusValue(StatusValue.CLIENT_HOLD)
|
||||
.hasStatusValue(CLIENT_HOLD)
|
||||
.and()
|
||||
.hasAuthInfoPwd("2BARfoo")
|
||||
.and()
|
||||
@@ -216,7 +221,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.and()
|
||||
.hasNoAutorenewEndTime();
|
||||
assertNoBillingEvents();
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
assertLastHistoryContainsResource(reloadResourceByForeignKey());
|
||||
}
|
||||
|
||||
@@ -345,7 +350,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertThat(domain.getContacts()).hasSize(3);
|
||||
assertThat(loadByKey(domain.getRegistrant()).getContactId()).isEqualTo("max_test_7");
|
||||
assertNoBillingEvents();
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -453,13 +458,13 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
persistDomain()
|
||||
.asBuilder()
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
|
||||
.setStatusValues(ImmutableSet.of(CLIENT_UPDATE_PROHIBITED))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlow();
|
||||
assertAboutDomains()
|
||||
.that(reloadResourceByForeignKey())
|
||||
.doesNotHaveStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED);
|
||||
.doesNotHaveStatusValue(CLIENT_UPDATE_PROHIBITED);
|
||||
}
|
||||
|
||||
private void doSecDnsSuccessfulTest(
|
||||
@@ -496,9 +501,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
.map(ds -> ds.cloneWithDomainRepoId(resource.getRepoId()))
|
||||
.collect(toImmutableSet()));
|
||||
if (dnsTaskEnqueued) {
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
} else {
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,8 +860,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
void testSuccess_noBillingOnPreExistingServerStatus() throws Exception {
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
Domain addStatusDomain = persistActiveDomain(getUniqueIdFromCommand());
|
||||
persistResource(
|
||||
addStatusDomain.asBuilder().addStatusValue(StatusValue.SERVER_RENEW_PROHIBITED).build());
|
||||
persistResource(addStatusDomain.asBuilder().addStatusValue(SERVER_RENEW_PROHIBITED).build());
|
||||
doServerStatusBillingTest("domain_update_add_server_status.xml", false);
|
||||
}
|
||||
|
||||
@@ -865,8 +869,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
persistReferencedEntities();
|
||||
Domain removeStatusDomain = persistDomain();
|
||||
persistResource(
|
||||
removeStatusDomain.asBuilder().addStatusValue(StatusValue.SERVER_RENEW_PROHIBITED).build());
|
||||
persistResource(removeStatusDomain.asBuilder().addStatusValue(SERVER_RENEW_PROHIBITED).build());
|
||||
doServerStatusBillingTest("domain_update_remove_server_status.xml", true);
|
||||
}
|
||||
|
||||
@@ -875,8 +878,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
eppRequestSource = EppRequestSource.TOOL;
|
||||
persistReferencedEntities();
|
||||
Domain changeStatusDomain = persistDomain();
|
||||
persistResource(
|
||||
changeStatusDomain.asBuilder().addStatusValue(StatusValue.SERVER_RENEW_PROHIBITED).build());
|
||||
persistResource(changeStatusDomain.asBuilder().addStatusValue(SERVER_RENEW_PROHIBITED).build());
|
||||
doServerStatusBillingTest("domain_update_change_server_status.xml", true);
|
||||
}
|
||||
|
||||
@@ -906,14 +908,14 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
persistActiveDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
|
||||
.setStatusValues(ImmutableSet.of(CLIENT_UPDATE_PROHIBITED))
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
runFlowAssertResponse(
|
||||
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("generic_success_response.xml"));
|
||||
assertAboutDomains()
|
||||
.that(reloadResourceByForeignKey())
|
||||
.hasStatusValue(StatusValue.CLIENT_UPDATE_PROHIBITED)
|
||||
.hasStatusValue(CLIENT_UPDATE_PROHIBITED)
|
||||
.and()
|
||||
.hasStatusValue(SERVER_HOLD);
|
||||
}
|
||||
@@ -1318,7 +1320,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_UPDATE_PROHIBITED))
|
||||
.setStatusValues(ImmutableSet.of(CLIENT_UPDATE_PROHIBITED))
|
||||
.build());
|
||||
EppException thrown =
|
||||
assertThrows(ResourceHasClientUpdateProhibitedException.class, this::runFlow);
|
||||
@@ -1345,7 +1347,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setDeletionTime(clock.nowUtc().plusDays(1))
|
||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
||||
.addStatusValue(PENDING_DELETE)
|
||||
.build());
|
||||
ResourceStatusProhibitsOperationException thrown =
|
||||
assertThrows(ResourceStatusProhibitsOperationException.class, this::runFlow);
|
||||
@@ -1500,7 +1502,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
loadByForeignKey(Contact.class, "mak21", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
||||
.addStatusValue(PENDING_DELETE)
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
LinkedResourceInPendingDeleteProhibitsOperationException thrown =
|
||||
@@ -1516,7 +1518,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
loadByForeignKey(Host.class, "ns2.example.foo", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.addStatusValue(StatusValue.PENDING_DELETE)
|
||||
.addStatusValue(PENDING_DELETE)
|
||||
.build());
|
||||
clock.advanceOneMilli();
|
||||
LinkedResourceInPendingDeleteProhibitsOperationException thrown =
|
||||
@@ -1681,9 +1683,7 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
doSuccessfulTest();
|
||||
assertAboutDomains()
|
||||
.that(reloadResourceByForeignKey())
|
||||
.hasExactlyStatusValues(StatusValue.CLIENT_HOLD);
|
||||
assertAboutDomains().that(reloadResourceByForeignKey()).hasExactlyStatusValues(CLIENT_HOLD);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1756,10 +1756,10 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistDomain()
|
||||
.asBuilder()
|
||||
.setDomainName("example.tld")
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.CLIENT_TRANSFER_PROHIBITED))
|
||||
.setStatusValues(ImmutableSet.of(CLIENT_TRANSFER_PROHIBITED))
|
||||
.build());
|
||||
runFlowAsSuperuser();
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1772,10 +1772,10 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistDomain()
|
||||
.asBuilder()
|
||||
.setDomainName("example.tld")
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.SERVER_HOLD))
|
||||
.setStatusValues(ImmutableSet.of(SERVER_HOLD))
|
||||
.build());
|
||||
runFlowAsSuperuser();
|
||||
dnsUtilsHelper.assertDomainDnsRequests("example.tld");
|
||||
assertDomainDnsRequests("example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1788,9 +1788,9 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
persistDomain()
|
||||
.asBuilder()
|
||||
.setDomainName("example.tld")
|
||||
.setStatusValues(ImmutableSet.of(StatusValue.PENDING_DELETE, StatusValue.SERVER_HOLD))
|
||||
.setStatusValues(ImmutableSet.of(PENDING_DELETE, SERVER_HOLD))
|
||||
.build());
|
||||
runFlowAsSuperuser();
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ package google.registry.flows.host;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.testing.DatabaseHelper.assertHostDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
@@ -116,7 +118,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
|
||||
void testSuccess_externalNeverExisted() throws Exception {
|
||||
doSuccessfulTest();
|
||||
assertAboutHosts().that(reloadResourceByForeignKey()).hasSuperordinateDomain(null);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -127,7 +129,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
|
||||
loadByForeignKey(Domain.class, "example.tld", clock.nowUtc()).get();
|
||||
assertAboutHosts().that(host).hasSuperordinateDomain(superordinateDomain.createVKey());
|
||||
assertThat(superordinateDomain.getSubordinateHosts()).containsExactly("ns1.example.tld");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns1.example.tld");
|
||||
assertHostDnsRequests("ns1.example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -144,7 +146,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
|
||||
persistDeletedHost(getUniqueIdFromCommand(), clock.nowUtc().minusDays(1));
|
||||
doSuccessfulTest();
|
||||
assertAboutHosts().that(reloadResourceByForeignKey()).hasSuperordinateDomain(null);
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,7 +158,7 @@ class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Host> {
|
||||
loadByForeignKey(Domain.class, "example.tld", clock.nowUtc()).get();
|
||||
assertAboutHosts().that(host).hasSuperordinateDomain(superordinateDomain.createVKey());
|
||||
assertThat(superordinateDomain.getSubordinateHosts()).containsExactly("ns1.example.tld");
|
||||
dnsUtilsHelper.assertHostDnsRequests("ns1.example.tld");
|
||||
assertHostDnsRequests("ns1.example.tld");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
package google.registry.flows.host;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.assertHostDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequests;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
@@ -314,10 +316,10 @@ class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, Host> {
|
||||
.hasType(Type.HOST_DELETE);
|
||||
assertNoBillingEvents();
|
||||
if (isSubordinate) {
|
||||
dnsUtilsHelper.assertHostDnsRequests(deletedHost.getHostName());
|
||||
assertHostDnsRequests(deletedHost.getHostName());
|
||||
assertThat(loadByKey(deletedHost.getSuperordinateDomain()).getSubordinateHosts()).isEmpty();
|
||||
} else {
|
||||
dnsUtilsHelper.assertNoMoreDnsRequests();
|
||||
assertNoDnsRequests();
|
||||
}
|
||||
assertLastHistoryContainsResource(deletedHost);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user