Fix bugs in load test action so that it actually works

This adds XSRF tokens so that the epptool request succeeds, adds better logging for debugging load test requests, adds better validation, and improves documentation.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=136885117
This commit is contained in:
mcilwain 2016-10-21 16:08:41 -07:00 committed by Ben McIlwain
parent 2e81de9954
commit 8e5bd45976
3 changed files with 70 additions and 18 deletions

View file

@ -21,6 +21,7 @@ java_library(
"//third_party/java/servlet/servlet_api",
"//java/google/registry/config",
"//java/google/registry/request",
"//java/google/registry/security",
"//java/google/registry/util",
],
)

View file

@ -19,6 +19,8 @@ import static com.google.appengine.api.taskqueue.QueueFactory.getQueue;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.partition;
import static com.google.common.collect.Lists.transform;
import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
import static google.registry.util.FormattingLogger.getLoggerForCallerClass;
import static google.registry.util.ResourceUtils.readResourceUtf8;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
@ -26,15 +28,14 @@ import static org.joda.time.DateTimeZone.UTC;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.net.MediaType;
import google.registry.config.RegistryEnvironment;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.security.XsrfTokenManager;
import google.registry.util.FormattingLogger;
import google.registry.util.TaskEnqueuer;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@ -45,13 +46,23 @@ import java.util.Random;
import javax.inject.Inject;
import org.joda.time.DateTime;
/** Simple load test action that can generate configurable QPSes of various EPP actions. */
/**
* Simple load test action that can generate configurable QPSes of various EPP actions.
*
* <p>All aspects of the load test are configured via URL parameters that are specified when the
* loadtest URL is being POSTed to. The {@code clientId} and {@code tld} parameters are required.
* All of the other parameters are optional, but if none are specified then no actual load testing
* will be done since all of the different kinds of checks default to running zero per second. So at
* least one must be specified in order for load testing to do anything.
*/
@Action(
path = "/_dr/loadtest",
method = Action.Method.POST,
automaticallyPrintOk = true)
public class LoadTestAction implements Runnable {
private static final FormattingLogger logger = getLoggerForCallerClass();
private static final int NUM_QUEUES = 10;
private static final int ARBITRARY_VALID_HOST_LENGTH = 40;
private static final int MAX_CONTACT_LENGTH = 13;
@ -149,6 +160,14 @@ public class LoadTestAction implements Runnable {
private final String xmlHostCreateFail;
private final String xmlHostInfo;
/**
* The XSRF token to be used for making requests to the epptool endpoint.
*
* <p>Note that the email address is set to empty, because the logged-in user hitting this
* endpoint will not be the same as when the tasks themselves fire and hit the epptool endpoint.
*/
private final String xsrfToken = XsrfTokenManager.generateToken("admin", "");
@Inject
LoadTestAction(@Parameter("tld") String tld) {
xmlContactCreateTmpl = loadXml("contact_create");
@ -171,10 +190,7 @@ public class LoadTestAction implements Runnable {
@Override
public void run() {
checkArgument(
RegistryEnvironment.get() != RegistryEnvironment.PRODUCTION,
"DO NOT RUN LOADTESTS IN PROD!");
validateAndLogRequest();
DateTime initialStartSecond = DateTime.now(UTC).plusSeconds(delaySeconds);
ImmutableList.Builder<String> preTaskXmls = new ImmutableList.Builder<>();
ImmutableList.Builder<String> contactNamesBuilder = new ImmutableList.Builder<>();
@ -236,7 +252,45 @@ public class LoadTestAction implements Runnable {
.toList(),
startSecond));
}
enqueue(tasks.build());
ImmutableList<TaskOptions> taskOptions = tasks.build();
enqueue(taskOptions);
logger.infofmt("Added %d total load test tasks", taskOptions.size());
}
private void validateAndLogRequest() {
checkArgument(
RegistryEnvironment.get() != RegistryEnvironment.PRODUCTION,
"DO NOT RUN LOADTESTS IN PROD!");
checkArgument(
successfulDomainCreatesPerSecond > 0
|| failedDomainCreatesPerSecond > 0
|| domainInfosPerSecond > 0
|| domainChecksPerSecond > 0
|| successfulContactCreatesPerSecond > 0
|| failedContactCreatesPerSecond > 0
|| contactInfosPerSecond > 0
|| successfulHostCreatesPerSecond > 0
|| failedHostCreatesPerSecond > 0
|| hostInfosPerSecond > 0,
"You must specify at least one of the 'operations per second' parameters.");
logger.infofmt(
"Running load test with the following params. clientId: %s, delaySeconds: %d, "
+ "runSeconds: %d, successful|failed domain creates/s: %d|%d, domain infos/s: %d, "
+ "domain checks/s: %d, successful|failed contact creates/s: %d|%d, "
+ "contact infos/s: %d, successful|failed host creates/s: %d|%d, host infos/s: %d.",
clientId,
delaySeconds,
runSeconds,
successfulDomainCreatesPerSecond,
failedDomainCreatesPerSecond,
domainInfosPerSecond,
domainChecksPerSecond,
successfulContactCreatesPerSecond,
failedContactCreatesPerSecond,
contactInfosPerSecond,
successfulHostCreatesPerSecond,
failedHostCreatesPerSecond,
hostInfosPerSecond);
}
private String loadXml(String name) {
@ -281,14 +335,11 @@ public class LoadTestAction implements Runnable {
int offsetMillis = (int) (1000.0 / xmls.size() * i);
tasks.add(TaskOptions.Builder.withUrl("/_dr/epptool")
.etaMillis(start.getMillis() + offsetMillis)
.payload(
Joiner.on('&').withKeyValueSeparator("=").join(
ImmutableMap.of(
"clientId", clientId,
"superuser", false,
"dryRun", false,
"xml", urlEncode(xmls.get(i)))),
MediaType.FORM_DATA.toString()));
.header(X_CSRF_TOKEN, xsrfToken)
.param("clientId", clientId)
.param("superuser", Boolean.FALSE.toString())
.param("dryRun", Boolean.FALSE.toString())
.param("xml", urlEncode(xmls.get(i))));
}
return tasks.build();
}