mirror of
https://github.com/google/nomulus.git
synced 2025-06-27 06:44:51 +02:00
mv com/google/domain/registry google/registry
This change renames directories in preparation for the great package rename. The repository is now in a broken state because the code itself hasn't been updated. However this should ensure that git correctly preserves history for each file.
This commit is contained in:
parent
a41677aea1
commit
5012893c1d
2396 changed files with 0 additions and 0 deletions
39
java/google/registry/tools/server/BUILD
Normal file
39
java/google/registry/tools/server/BUILD
Normal file
|
@ -0,0 +1,39 @@
|
|||
package(
|
||||
default_visibility = ["//java/com/google/domain/registry:registry_project"],
|
||||
)
|
||||
|
||||
|
||||
java_library(
|
||||
name = "server",
|
||||
srcs = glob(["*.java"]),
|
||||
deps = [
|
||||
"//java/com/google/common/annotations",
|
||||
"//java/com/google/common/base",
|
||||
"//java/com/google/common/collect",
|
||||
"//java/com/google/common/io",
|
||||
"//java/com/google/common/net",
|
||||
"//java/com/google/common/primitives",
|
||||
"//java/com/google/common/util/concurrent",
|
||||
"//java/com/google/domain/registry/config",
|
||||
"//java/com/google/domain/registry/export",
|
||||
"//java/com/google/domain/registry/flows",
|
||||
"//java/com/google/domain/registry/gcs",
|
||||
"//java/com/google/domain/registry/groups",
|
||||
"//java/com/google/domain/registry/mapreduce",
|
||||
"//java/com/google/domain/registry/mapreduce/inputs",
|
||||
"//java/com/google/domain/registry/model",
|
||||
"//java/com/google/domain/registry/request",
|
||||
"//java/com/google/domain/registry/util",
|
||||
"//third_party/java/appengine:appengine-api",
|
||||
"//third_party/java/appengine_gcs_client",
|
||||
"//third_party/java/appengine_mapreduce2:appengine_mapreduce",
|
||||
"//third_party/java/dagger",
|
||||
"//third_party/java/jcommander",
|
||||
"//third_party/java/joda_money",
|
||||
"//third_party/java/joda_time",
|
||||
"//third_party/java/jsr305_annotations",
|
||||
"//third_party/java/jsr330_inject",
|
||||
"//third_party/java/objectify:objectify-v4_1",
|
||||
"//third_party/java/servlet/servlet_api",
|
||||
],
|
||||
)
|
133
java/google/registry/tools/server/CreateGroupsAction.java
Normal file
133
java/google/registry/tools/server/CreateGroupsAction.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.export.SyncGroupMembersAction.getGroupEmailAddressForContactType;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
import static java.util.Arrays.asList;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.groups.GroupsConnection;
|
||||
import com.google.domain.registry.groups.GroupsConnection.Role;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.registrar.RegistrarContact;
|
||||
import com.google.domain.registry.model.registrar.RegistrarContact.Type;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.HttpException.BadRequestException;
|
||||
import com.google.domain.registry.request.HttpException.InternalServerErrorException;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.request.Response;
|
||||
import com.google.domain.registry.util.Concurrent;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Action that creates Google Groups for a registrar's mailing lists. */
|
||||
@Action(path = CreateGroupsAction.PATH, method = POST)
|
||||
public class CreateGroupsAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/admin/createGroups";
|
||||
public static final String CLIENT_ID_PARAM = "clientId";
|
||||
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
private static final int NUM_SIMULTANEOUS_CONNECTIONS = 5;
|
||||
|
||||
@Inject GroupsConnection groupsConnection;
|
||||
@Inject Response response;
|
||||
@Inject @Config("publicDomainName") String publicDomainName;
|
||||
@Inject @Parameter("clientId") Optional<String> clientId;
|
||||
@Inject CreateGroupsAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Registrar registrar = initAndLoadRegistrar();
|
||||
if (registrar == null) {
|
||||
return;
|
||||
}
|
||||
List<RegistrarContact.Type> types = asList(RegistrarContact.Type.values());
|
||||
// Concurrently create the groups for each RegistrarContact.Type, collecting the results from
|
||||
// each call (which are either an Exception if it failed, or absent() if it succeeded).
|
||||
List<Optional<Exception>> results = Concurrent.transform(
|
||||
types,
|
||||
NUM_SIMULTANEOUS_CONNECTIONS,
|
||||
new Function<RegistrarContact.Type, Optional<Exception>>() {
|
||||
@Override
|
||||
public Optional<Exception> apply(Type type) {
|
||||
try {
|
||||
String groupKey = getGroupEmailAddressForContactType(
|
||||
registrar.getClientIdentifier(), type, publicDomainName);
|
||||
String parentGroup =
|
||||
getGroupEmailAddressForContactType("registrar", type, publicDomainName);
|
||||
// Creates the group, then adds it as a member to the global registrar group for
|
||||
// that type.
|
||||
groupsConnection.createGroup(groupKey);
|
||||
groupsConnection.addMemberToGroup(parentGroup, groupKey, Role.MEMBER);
|
||||
return Optional.<Exception> absent();
|
||||
} catch (Exception e) {
|
||||
return Optional.of(e);
|
||||
}
|
||||
}});
|
||||
// Return the correct server response based on the results of the group creations.
|
||||
if (Optional.presentInstances(results).iterator().hasNext()) {
|
||||
StringWriter responseString = new StringWriter();
|
||||
PrintWriter responseWriter = new PrintWriter(responseString);
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
Optional<Exception> e = results.get(i);
|
||||
if (e.isPresent()) {
|
||||
responseWriter.append(types.get(i).getDisplayName()).append(" => ");
|
||||
e.get().printStackTrace(responseWriter);
|
||||
logger.severefmt(
|
||||
e.get(),
|
||||
"Could not create Google Group for registrar %s for type %s",
|
||||
registrar.getRegistrarName(),
|
||||
types.get(i).toString());
|
||||
} else {
|
||||
responseWriter.printf("%s => Success%n", types.get(i).getDisplayName());
|
||||
}
|
||||
}
|
||||
throw new InternalServerErrorException(responseString.toString());
|
||||
} else {
|
||||
response.setStatus(SC_OK);
|
||||
response.setPayload("Success!");
|
||||
logger.info("Successfully created groups for registrar: " + registrar.getRegistrarName());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Registrar initAndLoadRegistrar() {
|
||||
if (!clientId.isPresent()) {
|
||||
respondToBadRequest("Error creating Google Groups, missing parameter: clientId");
|
||||
}
|
||||
final Registrar registrar = Registrar.loadByClientId(clientId.get());
|
||||
if (registrar == null) {
|
||||
respondToBadRequest(String.format(
|
||||
"Error creating Google Groups; could not find registrar with id %s", clientId.get()));
|
||||
}
|
||||
return registrar;
|
||||
}
|
||||
|
||||
private void respondToBadRequest(String message) {
|
||||
logger.severe(message);
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.request.JsonResponse;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Abstract base class for actions that update premium lists.
|
||||
*/
|
||||
public abstract class CreateOrUpdatePremiumListAction implements Runnable {
|
||||
|
||||
protected static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
public static final String NAME_PARAM = "name";
|
||||
public static final String INPUT_PARAM = "inputData";
|
||||
|
||||
@Inject JsonResponse response;
|
||||
@Inject @Parameter(NAME_PARAM) String name;
|
||||
@Inject @Parameter(INPUT_PARAM) String inputData;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
savePremiumList();
|
||||
} catch (RuntimeException e) {
|
||||
logger.severe(e, e.getMessage());
|
||||
response.setPayload(ImmutableMap.of(
|
||||
"error", e.toString(),
|
||||
"status", "error"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a new premium list or updates an existing one. */
|
||||
protected abstract void savePremiumList();
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.model.registry.label.PremiumList;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* An action that creates a premium list, for use by the registry_tool create_premium_list command.
|
||||
*/
|
||||
@Action(path = CreatePremiumListAction.PATH, method = POST)
|
||||
public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
|
||||
public static final String OVERRIDE_PARAM = "override";
|
||||
public static final String PATH = "/_dr/admin/createPremiumList";
|
||||
|
||||
@Inject @Parameter(OVERRIDE_PARAM) boolean override;
|
||||
@Inject CreatePremiumListAction() {}
|
||||
|
||||
@Override
|
||||
protected void savePremiumList() {
|
||||
checkArgument(
|
||||
!PremiumList.exists(name),
|
||||
"A premium list of this name already exists: %s.", name);
|
||||
if (!override) {
|
||||
assertTldExists(name);
|
||||
}
|
||||
|
||||
logger.infofmt("Saving premium list for TLD %s", name);
|
||||
logger.infofmt("Got the following input data: %s", inputData);
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumList premiumList = new PremiumList.Builder()
|
||||
.setName(name)
|
||||
.setPremiumListMapFromLines(inputDataPreProcessed)
|
||||
.build();
|
||||
premiumList.saveAndUpdateEntries();
|
||||
|
||||
logger.infofmt("Saved premium list %s with entries %s",
|
||||
premiumList.getName(),
|
||||
premiumList.getPremiumListEntries());
|
||||
|
||||
response.setPayload(ImmutableMap.of("status", "success"));
|
||||
}
|
||||
}
|
118
java/google/registry/tools/server/DeleteEntityAction.java
Normal file
118
java/google/registry/tools/server/DeleteEntityAction.java
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.googlecode.objectify.Key.create;
|
||||
|
||||
import com.google.appengine.api.datastore.Entity;
|
||||
import com.google.appengine.api.datastore.EntityNotFoundException;
|
||||
import com.google.appengine.api.datastore.Key;
|
||||
import com.google.appengine.api.datastore.KeyFactory;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.HttpException.BadRequestException;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.request.Response;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
import com.googlecode.objectify.impl.EntityMetadata;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* An action to delete entities in Datastore specified by raw key ids, which can be found in
|
||||
* Datastore Viewer in the AppEngine console - it's the really long alphanumeric key that is
|
||||
* labeled "Entity key" on the page for an individual entity.
|
||||
*
|
||||
* <p>rawKeys is the only required parameter. It is a comma-delimited list of Strings.
|
||||
*
|
||||
* <p><b>WARNING:</b> This servlet can be dangerous if used incorrectly as it can bypass checks on
|
||||
* deletion (including whether the entity is referenced by other entities) and it does not write
|
||||
* commit log entries for non-registered types. It should mainly be used for deleting testing or
|
||||
* malformed data that cannot be properly deleted using existing tools. Generally, if there already
|
||||
* exists an entity-specific deletion command, then use that one instead.
|
||||
*/
|
||||
@Action(path = DeleteEntityAction.PATH)
|
||||
public class DeleteEntityAction implements Runnable {
|
||||
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
public static final String PATH = "/_dr/admin/deleteEntity";
|
||||
public static final String PARAM_RAW_KEYS = "rawKeys";
|
||||
|
||||
@Inject @Parameter(PARAM_RAW_KEYS) String rawKeys;
|
||||
@Inject Response response;
|
||||
@Inject DeleteEntityAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Get raw key strings from request
|
||||
ImmutableList.Builder<Object> ofyDeletionsBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<Key> rawDeletionsBuilder = new ImmutableList.Builder<>();
|
||||
for (String rawKeyString : Splitter.on(',').split(rawKeys)) {
|
||||
// Convert raw keys string to real keys. Try to load it from Objectify if it's a registered
|
||||
// type, and fall back to DatastoreService if its not registered.
|
||||
Key rawKey = KeyFactory.stringToKey(rawKeyString);
|
||||
Optional<Object> ofyEntity = loadOfyEntity(rawKey);
|
||||
if (ofyEntity.isPresent()) {
|
||||
ofyDeletionsBuilder.add(ofyEntity.get());
|
||||
continue;
|
||||
}
|
||||
Optional<Entity> rawEntity = loadRawEntity(rawKey);
|
||||
if (rawEntity.isPresent()) {
|
||||
rawDeletionsBuilder.add(rawEntity.get().getKey());
|
||||
continue;
|
||||
}
|
||||
// The entity could not be found by either Objectify or the datastore service
|
||||
throw new BadRequestException("Could not find entity with key " + rawKeyString);
|
||||
}
|
||||
// Delete raw entities.
|
||||
ImmutableList<Key> rawDeletions = rawDeletionsBuilder.build();
|
||||
getDatastoreService().delete(rawDeletions);
|
||||
// Delete ofy entities.
|
||||
final ImmutableList<Object> ofyDeletions = ofyDeletionsBuilder.build();
|
||||
ofy().transactNew(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
ofy().delete().entities(ofyDeletions).now();
|
||||
}});
|
||||
String message = String.format(
|
||||
"Deleted %d raw entities and %d registered entities",
|
||||
rawDeletions.size(),
|
||||
ofyDeletions.size());
|
||||
logger.info(message);
|
||||
response.setPayload(message);
|
||||
}
|
||||
|
||||
private Optional<Object> loadOfyEntity(Key rawKey) {
|
||||
EntityMetadata<Object> metadata = ofy().factory().getMetadata(rawKey.getKind());
|
||||
return Optional.fromNullable(metadata == null ? null : ofy().load().key(create(rawKey)).now());
|
||||
}
|
||||
|
||||
private Optional<Entity> loadRawEntity(Key rawKey) {
|
||||
try {
|
||||
return Optional.fromNullable(getDatastoreService().get(rawKey));
|
||||
} catch (EntityNotFoundException e) {
|
||||
logger.warningfmt(e, "Could not load entity from datastore service with key %s",
|
||||
rawKey.toString());
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
}
|
171
java/google/registry/tools/server/DeleteProberDataAction.java
Normal file
171
java/google/registry/tools/server/DeleteProberDataAction.java
Normal file
|
@ -0,0 +1,171 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Verify.verifyNotNull;
|
||||
import static com.google.domain.registry.mapreduce.MapreduceRunner.PARAM_DRY_RUN;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.getTldsOfType;
|
||||
|
||||
import com.google.appengine.tools.mapreduce.Mapper;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.domain.registry.mapreduce.MapreduceRunner;
|
||||
import com.google.domain.registry.mapreduce.inputs.EppResourceInputs;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainBase;
|
||||
import com.google.domain.registry.model.index.EppResourceIndex;
|
||||
import com.google.domain.registry.model.index.ForeignKeyIndex;
|
||||
import com.google.domain.registry.model.index.ForeignKeyIndex.ForeignKeyDomainIndex;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldType;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.request.Response;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
import com.google.domain.registry.util.PipelineUtils;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Work;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Deletes all prober DomainResources and their subordinate history entries, poll messages, and
|
||||
* billing events, along with their ForeignKeyDomainIndex and EppResourceIndex entities.
|
||||
*
|
||||
* <p>See: https://www.youtube.com/watch?v=xuuv0syoHnM
|
||||
*/
|
||||
@Action(path = "/_dr/task/deleteProberData")
|
||||
public class DeleteProberDataAction implements Runnable {
|
||||
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
@Inject @Parameter(PARAM_DRY_RUN) boolean isDryRun;
|
||||
@Inject MapreduceRunner mrRunner;
|
||||
@Inject Response response;
|
||||
@Inject DeleteProberDataAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
response.sendJavaScriptRedirect(PipelineUtils.createJobPath(mrRunner
|
||||
.setJobName("Delete prober data")
|
||||
// TODO(b/27309488): maybe move this to the backend module.
|
||||
.setModuleName("tools")
|
||||
.runMapOnly(
|
||||
new DeleteProberDataMapper(getProberRoidSuffixes(), isDryRun),
|
||||
ImmutableList.of(EppResourceInputs.createKeyInput(DomainBase.class)))));
|
||||
}
|
||||
|
||||
private static ImmutableSet<String> getProberRoidSuffixes() {
|
||||
return FluentIterable.from(getTldsOfType(TldType.TEST))
|
||||
.filter(new Predicate<String>() {
|
||||
@Override
|
||||
public boolean apply(String tld) {
|
||||
// Extra sanity check to prevent us from nuking prod data if a real TLD accidentally
|
||||
// gets set to type TEST.
|
||||
return tld.endsWith(".test");
|
||||
}})
|
||||
.transform(
|
||||
new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String tld) {
|
||||
return Registry.get(tld).getRoidSuffix();
|
||||
}})
|
||||
.toSet();
|
||||
}
|
||||
|
||||
/** Provides the map method that runs for each existing DomainBase entity. */
|
||||
public static class DeleteProberDataMapper extends Mapper<Key<DomainBase>, Void, Void> {
|
||||
|
||||
private static final long serialVersionUID = 1737761271804180412L;
|
||||
|
||||
private final ImmutableSet<String> proberRoidSuffixes;
|
||||
private final Boolean isDryRun;
|
||||
|
||||
public DeleteProberDataMapper(ImmutableSet<String> proberRoidSuffixes, Boolean isDryRun) {
|
||||
this.proberRoidSuffixes = proberRoidSuffixes;
|
||||
this.isDryRun = isDryRun;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void map(Key<DomainBase> key) {
|
||||
try {
|
||||
String roidSuffix = Iterables.getLast(Splitter.on('-').split(key.getName()));
|
||||
if (proberRoidSuffixes.contains(roidSuffix)) {
|
||||
deleteDomain(key);
|
||||
} else {
|
||||
getContext().incrementCounter(String.format("skipped, non-prober data"));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.severefmt(t, "Error while deleting prober data for key %s", key);
|
||||
getContext().incrementCounter(String.format("error, kind %s", key.getKind()));
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteDomain(final Key<DomainBase> domainKey) {
|
||||
final DomainBase domain = ofy().load().key(domainKey).now();
|
||||
if (domain == null) {
|
||||
// Depending on how stale Datastore indexes are, we can get keys to resources that are
|
||||
// already deleted (e.g. by a recent previous invocation of this mapreduce). So ignore them.
|
||||
getContext().incrementCounter("already deleted");
|
||||
return;
|
||||
}
|
||||
if (domain instanceof DomainApplication) {
|
||||
// Cover the case where we somehow have a domain application with a prober ROID suffix.
|
||||
getContext().incrementCounter("skipped, domain application");
|
||||
return;
|
||||
}
|
||||
if (domain.getFullyQualifiedDomainName().equals("nic." + domain.getTld())) {
|
||||
getContext().incrementCounter("skipped, NIC domain");
|
||||
return;
|
||||
}
|
||||
int dependentsDeleted = ofy().transact(new Work<Integer>() {
|
||||
@Override
|
||||
public Integer run() {
|
||||
EppResourceIndex eppIndex = ofy().load().entity(EppResourceIndex.create(domainKey)).now();
|
||||
verifyNotNull(eppIndex, "Missing EppResourceIndex for domain %s", domain);
|
||||
ForeignKeyIndex<?> fki = ofy().load().key(ForeignKeyDomainIndex.createKey(domain)).now();
|
||||
verifyNotNull(fki, "Missing ForeignKeyDomainIndex for domain %s", domain);
|
||||
// This ancestor query selects all descendant HistoryEntries, BillingEvents, and
|
||||
// PollMessages, as well as the domain itself.
|
||||
List<Key<Object>> domainAndDependentKeys = ofy().load().ancestor(domainKey).keys().list();
|
||||
if (isDryRun) {
|
||||
logger.infofmt(
|
||||
"Would delete the following entities: %s",
|
||||
new ImmutableList.Builder<Object>()
|
||||
.add(fki)
|
||||
.add(eppIndex)
|
||||
.addAll(domainAndDependentKeys)
|
||||
.build());
|
||||
} else {
|
||||
ofy().deleteWithoutBackup().keys(domainAndDependentKeys);
|
||||
ofy().deleteWithoutBackup().entities(eppIndex, fki);
|
||||
}
|
||||
return domainAndDependentKeys.size() - 1;
|
||||
}
|
||||
});
|
||||
getContext().incrementCounter(String.format("deleted, kind %s", domainKey.getKind()));
|
||||
getContext().incrementCounter("deleted, dependent keys", dependentsDeleted);
|
||||
}
|
||||
}
|
||||
}
|
333
java/google/registry/tools/server/GenerateZoneFilesAction.java
Normal file
333
java/google/registry/tools/server/GenerateZoneFilesAction.java
Normal file
|
@ -0,0 +1,333 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.appengine.tools.cloudstorage.GcsServiceFactory.createGcsService;
|
||||
import static com.google.common.base.Predicates.notNull;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Iterators.filter;
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.domain.registry.mapreduce.inputs.EppResourceInputs.createEntityInput;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadAtPointInTime;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
import static com.google.domain.registry.util.PipelineUtils.createJobPath;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.appengine.tools.cloudstorage.GcsFilename;
|
||||
import com.google.appengine.tools.cloudstorage.RetryParams;
|
||||
import com.google.appengine.tools.mapreduce.Mapper;
|
||||
import com.google.appengine.tools.mapreduce.Reducer;
|
||||
import com.google.appengine.tools.mapreduce.ReducerInput;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.gcs.GcsUtils;
|
||||
import com.google.domain.registry.mapreduce.MapreduceRunner;
|
||||
import com.google.domain.registry.mapreduce.inputs.NullInput;
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.ReferenceUnion;
|
||||
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.HttpException.BadRequestException;
|
||||
import com.google.domain.registry.request.JsonActionRunner;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
|
||||
import com.googlecode.objectify.Ref;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* MapReduce that requests generation of BIND zone files for a set of TLDs at a given time.
|
||||
*
|
||||
* <p>Zone files for each requested TLD are written to GCS. TLDs without entries produce zone files
|
||||
* with only a header. The export time must be at least two minutes in the past and no more than
|
||||
* 29 days in the past, and must be at midnight UTC.
|
||||
*/
|
||||
@Action(
|
||||
path = GenerateZoneFilesAction.PATH,
|
||||
method = POST,
|
||||
xsrfProtection = true,
|
||||
xsrfScope = "admin")
|
||||
public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonAction {
|
||||
|
||||
public static final String PATH = "/_dr/task/generateZoneFiles";
|
||||
|
||||
/** Format for the zone file name. */
|
||||
private static final String FILENAME_FORMAT = "%s-%s.zone";
|
||||
|
||||
/** Format for the GCS path to a file. */
|
||||
private static final String GCS_PATH_FORMAT = "gs://%s/%s";
|
||||
|
||||
/** Format for the zone file header. */
|
||||
private static final String HEADER_FORMAT = "$ORIGIN\t%s.\n\n";
|
||||
|
||||
/** Format for NS records. */
|
||||
private static final String NS_FORMAT = "%s\t%d\tIN\tNS\t%s.\n";
|
||||
|
||||
/** Format for DS records. */
|
||||
private static final String DS_FORMAT = "%s\t%d\tIN\tDS\t%d %d %d %s\n";
|
||||
|
||||
/** Format for A and AAAA records. */
|
||||
private static final String A_FORMAT = "%s\t%d\tIN\t%s\t%s\n";
|
||||
|
||||
// TODO(b/20454352): Overhaul TTL configuration mechanism.
|
||||
/** The time to live for exported NS record, in seconds. */
|
||||
private static final int TTL_NS = 180;
|
||||
|
||||
/** The time to live for exported DS record, in seconds. */
|
||||
private static final int TTL_DS = 86400;
|
||||
|
||||
/** The time to live for exported A/AAAA record, in seconds. */
|
||||
private static final int TTL_A = 3600;
|
||||
|
||||
@Inject MapreduceRunner mrRunner;
|
||||
@Inject JsonActionRunner jsonActionRunner;
|
||||
@Inject @Config("zoneFilesBucket") String bucket;
|
||||
@Inject @Config("gcsBufferSize") int gcsBufferSize;
|
||||
@Inject @Config("commitLogDatastoreRetention") Duration datastoreRetention;
|
||||
@Inject Clock clock;
|
||||
@Inject GenerateZoneFilesAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> handleJsonRequest(Map<String, ?> json) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ImmutableSet<String> tlds = ImmutableSet.copyOf((List<String>) json.get("tlds"));
|
||||
final DateTime exportTime = DateTime.parse(json.get("exportTime").toString());
|
||||
// We disallow exporting within the past 2 minutes because there might be outstanding writes.
|
||||
// We can only reliably call loadAtPointInTime at times that are UTC midnight and >
|
||||
// datastoreRetention ago in the past.
|
||||
DateTime now = clock.nowUtc();
|
||||
if (exportTime.isAfter(now.minusMinutes(2))) {
|
||||
throw new BadRequestException("Invalid export time: must be > 2 minutes ago");
|
||||
}
|
||||
if (exportTime.isBefore(now.minus(datastoreRetention))) {
|
||||
throw new BadRequestException(String.format(
|
||||
"Invalid export time: must be < %d days ago",
|
||||
datastoreRetention.getStandardDays()));
|
||||
}
|
||||
if (!exportTime.equals(exportTime.toDateTime(UTC).withTimeAtStartOfDay())) {
|
||||
throw new BadRequestException("Invalid export time: must be midnight UTC");
|
||||
}
|
||||
String jobId = mrRunner
|
||||
.setJobName("Generate bind file stanzas")
|
||||
.setModuleName("tools")
|
||||
.setDefaultReduceShards(tlds.size())
|
||||
.runMapreduce(
|
||||
new GenerateBindFileMapper(tlds, exportTime),
|
||||
new GenerateBindFileReducer(bucket, exportTime, gcsBufferSize),
|
||||
ImmutableList.of(
|
||||
new NullInput<EppResource>(),
|
||||
createEntityInput(DomainResource.class, HostResource.class)));
|
||||
ImmutableList<String> filenames = FluentIterable.from(tlds)
|
||||
.transform(
|
||||
new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String tld) {
|
||||
return String.format(
|
||||
GCS_PATH_FORMAT,
|
||||
bucket,
|
||||
String.format(FILENAME_FORMAT, tld, exportTime));
|
||||
}})
|
||||
.toList();
|
||||
return ImmutableMap.<String, Object>of(
|
||||
"jobPath", createJobPath(jobId),
|
||||
"filenames", filenames);
|
||||
}
|
||||
|
||||
/** Mapper to find domains and hosts that were active at a given time. */
|
||||
static class GenerateBindFileMapper extends Mapper<EppResource, String, String> {
|
||||
|
||||
private static final long serialVersionUID = 4647941823789859913L;
|
||||
|
||||
private final ImmutableSet<String> tlds;
|
||||
private final DateTime exportTime;
|
||||
|
||||
GenerateBindFileMapper(ImmutableSet<String> tlds, DateTime exportTime) {
|
||||
this.tlds = tlds;
|
||||
this.exportTime = exportTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void map(EppResource resource) {
|
||||
if (resource == null) { // Force the reducer to always generate a bind header for each tld.
|
||||
for (String tld : tlds) {
|
||||
emit(tld, null);
|
||||
}
|
||||
} else if (resource instanceof DomainResource) {
|
||||
mapDomain((DomainResource) resource);
|
||||
} else {
|
||||
mapHost((HostResource) resource);
|
||||
}
|
||||
}
|
||||
|
||||
private void mapDomain(DomainResource domain) {
|
||||
// Domains never change their tld, so we can check if it's from the wrong tld right away.
|
||||
if (tlds.contains(domain.getTld())) {
|
||||
domain = loadAtPointInTime(domain, exportTime).now();
|
||||
if (domain != null) { // A null means the domain was deleted (or not created) at this time.
|
||||
String stanza = domainStanza(domain, exportTime);
|
||||
if (!stanza.isEmpty()) {
|
||||
emit(domain.getTld(), stanza);
|
||||
getContext().incrementCounter(domain.getTld() + " domains");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mapHost(HostResource host) {
|
||||
host = loadAtPointInTime(host, exportTime).now();
|
||||
if (host != null) { // A null means the host was deleted (or not created) at this time.
|
||||
// Find a matching tld. Hosts might change their tld, so check after the point-in-time load.
|
||||
String fullyQualifiedHostName = host.getFullyQualifiedHostName();
|
||||
for (String tld : tlds) {
|
||||
if (fullyQualifiedHostName.endsWith("." + tld)) {
|
||||
String stanza = hostStanza(host);
|
||||
if (!stanza.isEmpty()) {
|
||||
emit(tld, stanza);
|
||||
getContext().incrementCounter(tld + " hosts");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Reducer to write zone files to GCS. */
|
||||
static class GenerateBindFileReducer extends Reducer<String, String, Void> {
|
||||
|
||||
private static final long serialVersionUID = -8489050680083119352L;
|
||||
|
||||
private final String bucket;
|
||||
private final DateTime exportTime;
|
||||
private final int gcsBufferSize;
|
||||
|
||||
GenerateBindFileReducer(String bucket, DateTime exportTime, int gcsBufferSize) {
|
||||
this.bucket = bucket;
|
||||
this.exportTime = exportTime;
|
||||
this.gcsBufferSize = gcsBufferSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reduce(String tld, ReducerInput<String> stanzas) {
|
||||
String stanzaCounter = tld + " stanzas";
|
||||
GcsFilename filename =
|
||||
new GcsFilename(bucket, String.format(FILENAME_FORMAT, tld, exportTime));
|
||||
GcsUtils cloudStorage =
|
||||
new GcsUtils(createGcsService(RetryParams.getDefaultInstance()), gcsBufferSize);
|
||||
try (OutputStream gcsOutput = cloudStorage.openOutputStream(filename);
|
||||
Writer osWriter = new OutputStreamWriter(gcsOutput, UTF_8);
|
||||
PrintWriter writer = new PrintWriter(osWriter)) {
|
||||
writer.printf(HEADER_FORMAT, tld);
|
||||
for (Iterator<String> stanzaIter = filter(stanzas, notNull()); stanzaIter.hasNext(); ) {
|
||||
writer.println(stanzaIter.next());
|
||||
getContext().incrementCounter(stanzaCounter);
|
||||
}
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates DNS records for a domain (NS and DS).
|
||||
*
|
||||
* These look like this:
|
||||
* {@code
|
||||
* foo.tld 180 IN NS ns.example.com.
|
||||
* foo.tld 86400 IN DS 1 2 3 000102
|
||||
* }
|
||||
*/
|
||||
private static String domainStanza(DomainResource domain, DateTime exportTime) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (HostResource nameserver : ofy().load().refs(
|
||||
transform(
|
||||
domain.getNameservers(),
|
||||
new Function<ReferenceUnion<HostResource>, Ref<HostResource>>() {
|
||||
@Override
|
||||
public Ref<HostResource> apply(ReferenceUnion<HostResource> referenceUnion) {
|
||||
return referenceUnion.getLinked();
|
||||
}})).values()) {
|
||||
result.append(String.format(
|
||||
NS_FORMAT,
|
||||
domain.getFullyQualifiedDomainName(),
|
||||
TTL_NS,
|
||||
// Load the nameservers at the export time in case they've been renamed or deleted.
|
||||
loadAtPointInTime(nameserver, exportTime).now().getFullyQualifiedHostName()));
|
||||
}
|
||||
for (DelegationSignerData dsData : domain.getDsData()) {
|
||||
result.append(String.format(
|
||||
DS_FORMAT,
|
||||
domain.getFullyQualifiedDomainName(),
|
||||
TTL_DS,
|
||||
dsData.getKeyTag(),
|
||||
dsData.getAlgorithm(),
|
||||
dsData.getDigestType(),
|
||||
base16().encode((dsData.getDigest()))));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates DNS records for a domain (A and AAAA).
|
||||
*
|
||||
* <p>These look like this:
|
||||
* {@code
|
||||
* ns.foo.tld 3600 IN A 127.0.0.1
|
||||
* ns.foo.tld 3600 IN AAAA 0:0:0:0:0:0:0:1
|
||||
* }
|
||||
*/
|
||||
private static String hostStanza(HostResource host) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (InetAddress addr : host.getInetAddresses()) {
|
||||
// must be either IPv4 or IPv6
|
||||
String rrSetClass = (addr instanceof Inet4Address) ? "A" : "AAAA";
|
||||
result.append(String.format(
|
||||
A_FORMAT,
|
||||
host.getFullyQualifiedHostName(),
|
||||
TTL_A,
|
||||
rrSetClass,
|
||||
addr.getHostAddress()));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
100
java/google/registry/tools/server/KillAllCommitLogsAction.java
Normal file
100
java/google/registry/tools/server/KillAllCommitLogsAction.java
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.Lists.partition;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
import static com.google.domain.registry.util.PipelineUtils.createJobPath;
|
||||
|
||||
import com.google.appengine.tools.mapreduce.Mapper;
|
||||
import com.google.appengine.tools.mapreduce.inputs.InMemoryInput;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
import com.google.domain.registry.mapreduce.MapreduceAction;
|
||||
import com.google.domain.registry.mapreduce.MapreduceRunner;
|
||||
import com.google.domain.registry.model.ofy.CommitLogBucket;
|
||||
import com.google.domain.registry.model.ofy.CommitLogCheckpointRoot;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Response;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Deletes all commit logs in datastore. */
|
||||
@Action(path = "/_dr/task/killAllCommitLogs", method = POST)
|
||||
public class KillAllCommitLogsAction implements MapreduceAction {
|
||||
|
||||
@Inject MapreduceRunner mrRunner;
|
||||
@Inject Response response;
|
||||
@Inject KillAllCommitLogsAction() {}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
checkArgument( // safety
|
||||
RegistryEnvironment.get() == RegistryEnvironment.CRASH
|
||||
|| RegistryEnvironment.get() == RegistryEnvironment.UNITTEST,
|
||||
"DO NOT RUN ANYWHERE ELSE EXCEPT CRASH OR TESTS.");
|
||||
response.sendJavaScriptRedirect(createJobPath(mrRunner
|
||||
.setJobName("Delete all commit logs and checkpoints")
|
||||
.setModuleName("tools")
|
||||
.runMapreduce(
|
||||
new KillAllCommitLogsMapper(),
|
||||
new KillAllEntitiesReducer(),
|
||||
// Create a in-memory input, assigning each bucket to its own shard for maximum
|
||||
// parallelization, with one extra shard for the CommitLogCheckpointRoot.
|
||||
ImmutableList.of(
|
||||
new InMemoryInput<>(
|
||||
partition(
|
||||
FluentIterable
|
||||
.from(Arrays.<Key<?>>asList(CommitLogCheckpointRoot.getKey()))
|
||||
.append(CommitLogBucket.getAllBucketKeys())
|
||||
.toList(),
|
||||
1))))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapper to delete a {@link CommitLogBucket} or {@link CommitLogCheckpointRoot} and any commit
|
||||
* logs or checkpoints that descend from it.
|
||||
*
|
||||
* <p>This will delete:
|
||||
* <ul>
|
||||
* <li>{@link CommitLogBucket}
|
||||
* <li>{@code CommitLogCheckpoint}
|
||||
* <li>{@link CommitLogCheckpointRoot}
|
||||
* <li>{@code CommitLogManifest}
|
||||
* <li>{@code CommitLogMutation}
|
||||
* </ul>
|
||||
*/
|
||||
static class KillAllCommitLogsMapper extends Mapper<Key<?>, Key<?>, Key<?>> {
|
||||
|
||||
private static final long serialVersionUID = 1504266335352952033L;
|
||||
|
||||
@Override
|
||||
public void map(Key<?> bucketOrRoot) {
|
||||
for (Key<Object> key : ofy().load().ancestor(bucketOrRoot).keys()) {
|
||||
emit(bucketOrRoot, key);
|
||||
getContext().incrementCounter("entities emitted");
|
||||
getContext().incrementCounter(String.format("%s emitted", key.getKind()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
import static com.google.domain.registry.util.PipelineUtils.createJobPath;
|
||||
|
||||
import com.google.appengine.tools.mapreduce.Mapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
import com.google.domain.registry.mapreduce.MapreduceAction;
|
||||
import com.google.domain.registry.mapreduce.MapreduceRunner;
|
||||
import com.google.domain.registry.mapreduce.inputs.NullInput;
|
||||
import com.google.domain.registry.model.common.EntityGroupRoot;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Response;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Deletes all cross tld entities in datastore.
|
||||
*
|
||||
* <p>This doesn't really need to be a mapreduce, but doing so makes it consistent with the other
|
||||
* kill-all actions, and gives us retries, a dashboard and counters for free.
|
||||
*/
|
||||
@Action(path = "/_dr/task/killAllCrossTld", method = POST)
|
||||
public class KillAllCrossTldEntitiesAction implements MapreduceAction {
|
||||
|
||||
@Inject MapreduceRunner mrRunner;
|
||||
@Inject Response response;
|
||||
@Inject KillAllCrossTldEntitiesAction() {}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
checkArgument( // safety
|
||||
RegistryEnvironment.get() == RegistryEnvironment.CRASH
|
||||
|| RegistryEnvironment.get() == RegistryEnvironment.UNITTEST,
|
||||
"DO NOT RUN ANYWHERE ELSE EXCEPT CRASH OR TESTS.");
|
||||
response.sendJavaScriptRedirect(createJobPath(mrRunner
|
||||
.setJobName("Delete all cross-tld entities")
|
||||
.setModuleName("tools")
|
||||
.runMapreduce(
|
||||
new KillAllCrossTldEntitiesMapper(),
|
||||
new KillAllEntitiesReducer(),
|
||||
ImmutableList.of(new NullInput<Object>()))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapper to delete all descendants of {@link EntityGroupRoot#getCrossTldKey()}.
|
||||
*
|
||||
* <p>This will delete:
|
||||
* <ul>
|
||||
* <i>{@code ClaimsListShard}
|
||||
* <i>{@code ClaimsListSingleton}
|
||||
* <i>{@link EntityGroupRoot}
|
||||
* <i>{@code LogsExportCursor}
|
||||
* <i>{@code PremiumList}
|
||||
* <i>{@code PremiumListEntry}
|
||||
* <i>{@code Registrar}
|
||||
* <i>{@code RegistrarBillingEntry}
|
||||
* <i>{@code RegistrarContact}
|
||||
* <i>{@code RegistrarCredit}
|
||||
* <i>{@code RegistrarCreditBalance}
|
||||
* <i>{@code Registry}
|
||||
* <i>{@code RegistryCursor}
|
||||
* <i>{@code ReservedList}
|
||||
* <i>{@code ServerSecret}
|
||||
* <i>{@code SignedMarkRevocationList}
|
||||
* <i>{@code TmchCrl}
|
||||
* </ul>
|
||||
*/
|
||||
static class KillAllCrossTldEntitiesMapper extends Mapper<Object, Key<?>, Key<?>> {
|
||||
|
||||
private static final long serialVersionUID = 8343696167876596542L;
|
||||
|
||||
@Override
|
||||
public void map(Object ignored) {
|
||||
// There will be exactly one input to the mapper, and we ignore it.
|
||||
Key<EntityGroupRoot> crossTldKey = getCrossTldKey();
|
||||
for (Key<Object> key : ofy().load().ancestor(crossTldKey).keys()) {
|
||||
emit(crossTldKey, key);
|
||||
getContext().incrementCounter("entities emitted");
|
||||
getContext().incrementCounter(String.format("%s emitted", key.getKind()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.collect.Iterators.partition;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.appengine.tools.mapreduce.Reducer;
|
||||
import com.google.appengine.tools.mapreduce.ReducerInput;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/** Reducer that deletes a group of keys, identified by a shared ancestor key. */
|
||||
public class KillAllEntitiesReducer extends Reducer<Key<?>, Key<?>, Void> {
|
||||
|
||||
private static final long serialVersionUID = 7939357855356876000L;
|
||||
|
||||
private static final int BATCH_SIZE = 100;
|
||||
|
||||
@Override
|
||||
public void reduce(Key<?> ancestor, final ReducerInput<Key<?>> keysToDelete) {
|
||||
Iterator<List<Key<?>>> batches = partition(keysToDelete, BATCH_SIZE);
|
||||
while (batches.hasNext()) {
|
||||
final List<Key<?>> batch = batches.next();
|
||||
// Use a transaction to get retrying for free.
|
||||
ofy().transact(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
ofy().deleteWithoutBackup().keys(batch);
|
||||
}});
|
||||
getContext().incrementCounter("entities deleted", batch.size());
|
||||
for (Key<?> key : batch) {
|
||||
getContext().incrementCounter(String.format("%s deleted", key.getKind()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
java/google/registry/tools/server/KillAllEppResourcesAction.java
Normal file
113
java/google/registry/tools/server/KillAllEppResourcesAction.java
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
import static com.google.domain.registry.util.PipelineUtils.createJobPath;
|
||||
|
||||
import com.google.appengine.tools.mapreduce.Mapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
import com.google.domain.registry.mapreduce.MapreduceAction;
|
||||
import com.google.domain.registry.mapreduce.MapreduceRunner;
|
||||
import com.google.domain.registry.mapreduce.inputs.EppResourceInputs;
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.index.DomainApplicationIndex;
|
||||
import com.google.domain.registry.model.index.EppResourceIndex;
|
||||
import com.google.domain.registry.model.index.ForeignKeyIndex;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Response;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Work;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Deletes all {@link EppResource} objects in datastore, including indices and descendants. */
|
||||
@Action(path = "/_dr/task/killAllEppResources", method = POST)
|
||||
public class KillAllEppResourcesAction implements MapreduceAction {
|
||||
|
||||
@Inject MapreduceRunner mrRunner;
|
||||
@Inject Response response;
|
||||
@Inject KillAllEppResourcesAction() {}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
checkArgument( // safety
|
||||
RegistryEnvironment.get() == RegistryEnvironment.CRASH
|
||||
|| RegistryEnvironment.get() == RegistryEnvironment.UNITTEST,
|
||||
"DO NOT RUN ANYWHERE ELSE EXCEPT CRASH OR TESTS.");
|
||||
response.sendJavaScriptRedirect(createJobPath(mrRunner
|
||||
.setJobName("Delete all EppResources, children, and indices")
|
||||
.setModuleName("tools")
|
||||
.runMapreduce(
|
||||
new KillAllEppResourcesMapper(),
|
||||
new KillAllEntitiesReducer(),
|
||||
ImmutableList.of(EppResourceInputs.createIndexInput()))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapper to delete an {@link EppResourceIndex}, its referent, all descendants of each referent,
|
||||
* and the {@link ForeignKeyIndex} or {@link DomainApplicationIndex} of the referent, as
|
||||
* appropriate.
|
||||
*
|
||||
* <p>This will delete:
|
||||
* <ul>
|
||||
* <li>All {@link ForeignKeyIndex} types
|
||||
* <li>{@link DomainApplicationIndex}
|
||||
* <li>{@link EppResourceIndex}
|
||||
* <li>All {@link EppResource} types
|
||||
* <li>{@code HistoryEntry}
|
||||
* <li>All {@code BillingEvent} types
|
||||
* <li>All {@code PollMessage} types
|
||||
* </ul>
|
||||
*/
|
||||
static class KillAllEppResourcesMapper extends Mapper<EppResourceIndex, Key<?>, Key<?>> {
|
||||
|
||||
private static final long serialVersionUID = 8205309000002507407L;
|
||||
|
||||
@Override
|
||||
public void map(final EppResourceIndex eri) {
|
||||
Key<EppResourceIndex> eriKey = Key.create(eri);
|
||||
emitAndIncrementCounter(eriKey, eriKey);
|
||||
Key<?> resourceKey = eri.getReference().getKey();
|
||||
for (Key<Object> key : ofy().load().ancestor(resourceKey).keys()) {
|
||||
emitAndIncrementCounter(resourceKey, key);
|
||||
}
|
||||
// Load in a transaction to make sure we don't get stale data (in case of host renames).
|
||||
// TODO(b/27424173): A transaction is overkill. When we have memcache-skipping, use that.
|
||||
EppResource resource = ofy().transactNewReadOnly(
|
||||
new Work<EppResource>() {
|
||||
@Override
|
||||
public EppResource run() {
|
||||
return eri.getReference().get();
|
||||
}});
|
||||
// TODO(b/28247733): What about FKI's for renamed hosts?
|
||||
Key<?> indexKey = resource instanceof DomainApplication
|
||||
? DomainApplicationIndex.createKey((DomainApplication) resource)
|
||||
: ForeignKeyIndex.createKey(resource);
|
||||
emitAndIncrementCounter(indexKey, indexKey);
|
||||
}
|
||||
|
||||
private void emitAndIncrementCounter(Key<?> ancestor, Key<?> child) {
|
||||
emit(ancestor, child);
|
||||
getContext().incrementCounter("entities emitted");
|
||||
getContext().incrementCounter(String.format("%s emitted", child.getKind()));
|
||||
}
|
||||
}
|
||||
}
|
59
java/google/registry/tools/server/ListDomainsAction.java
Normal file
59
java/google/registry/tools/server/ListDomainsAction.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.model.EppResourceUtils.queryNotDeleted;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.request.Action.Method.GET;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** An action that lists domains, for use by the registry_tool list_domains command. */
|
||||
@Action(path = ListDomainsAction.PATH, method = {GET, POST})
|
||||
public final class ListDomainsAction extends ListObjectsAction<DomainResource> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/domains";
|
||||
public static final String TLD_PARAM = "tld";
|
||||
|
||||
@Inject @Parameter("tld") String tld;
|
||||
@Inject Clock clock;
|
||||
@Inject ListDomainsAction() {}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("fullyQualifiedDomainName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<DomainResource> loadObjects() {
|
||||
return FluentIterable
|
||||
.from(queryNotDeleted(DomainResource.class, clock.nowUtc(), "tld", assertTldExists(tld)))
|
||||
.toSortedSet(new Comparator<DomainResource>() {
|
||||
@Override
|
||||
public int compare(DomainResource a, DomainResource b) {
|
||||
return a.getFullyQualifiedDomainName().compareTo(b.getFullyQualifiedDomainName());
|
||||
}});
|
||||
}
|
||||
}
|
69
java/google/registry/tools/server/ListHostsAction.java
Normal file
69
java/google/registry/tools/server/ListHostsAction.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.request.Action.Method.GET;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.EppResourceUtils;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** An action that lists hosts, for use by the registry_tool list_hosts command. */
|
||||
@Action(path = ListHostsAction.PATH, method = {GET, POST})
|
||||
public final class ListHostsAction extends ListObjectsAction<HostResource> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/hosts";
|
||||
|
||||
private static final Comparator<HostResource> comparator =
|
||||
new Comparator<HostResource>() {
|
||||
@Override
|
||||
public int compare(HostResource host1, HostResource host2) {
|
||||
return host1.getFullyQualifiedHostName()
|
||||
.compareTo(host2.getFullyQualifiedHostName());
|
||||
}};
|
||||
|
||||
@Inject Clock clock;
|
||||
@Inject ListHostsAction() {}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("fullyQualifiedHostName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<HostResource> loadObjects() {
|
||||
final DateTime now = clock.nowUtc();
|
||||
return FluentIterable
|
||||
.from(ofy().load().type(HostResource.class))
|
||||
.filter(new Predicate<HostResource>() {
|
||||
@Override
|
||||
public boolean apply(HostResource host) {
|
||||
return EppResourceUtils.isActive(host, now);
|
||||
}})
|
||||
.toSortedSet(comparator);
|
||||
}
|
||||
}
|
286
java/google/registry/tools/server/ListObjectsAction.java
Normal file
286
java/google/registry/tools/server/ListObjectsAction.java
Normal file
|
@ -0,0 +1,286 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.ImmutableTable;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.domain.registry.model.ImmutableObject;
|
||||
import com.google.domain.registry.request.JsonResponse;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Abstract base class for actions that list ImmutableObjects.
|
||||
*
|
||||
* <p>Returns formatted text to be displayed on the screen.
|
||||
*
|
||||
* @param <T> type of object
|
||||
*/
|
||||
public abstract class ListObjectsAction<T extends ImmutableObject> implements Runnable {
|
||||
|
||||
public static final String FIELDS_PARAM = "fields";
|
||||
public static final String PRINT_HEADER_ROW_PARAM = "printHeaderRow";
|
||||
public static final String FULL_FIELD_NAMES_PARAM = "fullFieldNames";
|
||||
|
||||
@Inject JsonResponse response;
|
||||
@Inject @Parameter("fields") Optional<String> fields;
|
||||
@Inject @Parameter("printHeaderRow") Optional<Boolean> printHeaderRow;
|
||||
@Inject @Parameter("fullFieldNames") Optional<Boolean> fullFieldNames;
|
||||
|
||||
/** Returns the set of objects to list, in the desired listing order. */
|
||||
abstract ImmutableSet<T> loadObjects();
|
||||
|
||||
/**
|
||||
* Returns a set of fields to always include in the output as the leftmost columns. Subclasses
|
||||
* can use this to specify the equivalent of a "primary key" for each object listed.
|
||||
*/
|
||||
ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link ImmutableBiMap} that maps any field name aliases to the actual field names.
|
||||
* <p>
|
||||
* Users can select aliased fields for display using either the original name or the alias. By
|
||||
* default, aliased fields will use the alias name as the header instead of the original name.
|
||||
*/
|
||||
ImmutableBiMap<String, String> getFieldAliases() {
|
||||
return ImmutableBiMap.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns for a given {@link ImmutableObject} a mapping from field names to field values that
|
||||
* will override, for any overlapping field names, the default behavior of getting the field
|
||||
* value by looking up that field name in the map returned by
|
||||
* {@link ImmutableObject#toDiffableFieldMap}.
|
||||
* <p>
|
||||
* This can be used to specify customized printing of certain fields (e.g. to print out a boolean
|
||||
* field as "active" or "-" instead of "true" or "false"). It can also be used to add fields to
|
||||
* the data, e.g. for computed fields that can be accessed from the object directly but aren't
|
||||
* stored as simple fields.
|
||||
*/
|
||||
ImmutableMap<String, String> getFieldOverrides(@SuppressWarnings("unused") T object) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Get the object data first, so we can figure out the list of all available fields using the
|
||||
// data if necessary.
|
||||
ImmutableSet<T> objects = loadObjects();
|
||||
// Get the list of fields we should return.
|
||||
ImmutableSet<String> fieldsToUse = getFieldsToUse(objects);
|
||||
// Convert the data into a table.
|
||||
ImmutableTable<T, String, String> data = extractData(fieldsToUse, objects);
|
||||
// Now that we have the data table, compute the column widths.
|
||||
ImmutableMap<String, Integer> columnWidths =
|
||||
computeColumnWidths(data, isHeaderRowInUse(data));
|
||||
// Finally, convert the table to an array of lines of text.
|
||||
List<String> lines = generateFormattedData(data, columnWidths);
|
||||
// Return the results.
|
||||
response.setPayload(ImmutableMap.of(
|
||||
"lines", lines,
|
||||
"status", "success"));
|
||||
} catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
if (message == null) {
|
||||
message = e.getClass().getName();
|
||||
}
|
||||
response.setPayload(ImmutableMap.of(
|
||||
"error", message,
|
||||
"status", "error"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of fields to return, aliased or not according to --full_field_names, and
|
||||
* with duplicates eliminated but the ordering otherwise preserved.
|
||||
*/
|
||||
private ImmutableSet<String> getFieldsToUse(ImmutableSet<T> objects) {
|
||||
// Get the list of fields from the received parameter.
|
||||
List<String> fieldsToUse;
|
||||
if ((fields == null) || !fields.isPresent()) {
|
||||
fieldsToUse = new ArrayList<>();
|
||||
} else {
|
||||
fieldsToUse = Splitter.on(',').splitToList(fields.get());
|
||||
// Check whether any field name is the wildcard; if so, use all fields.
|
||||
if (fieldsToUse.contains("*")) {
|
||||
fieldsToUse = getAllAvailableFields(objects);
|
||||
}
|
||||
}
|
||||
// Handle aliases according to the state of the fullFieldNames parameter.
|
||||
final ImmutableMap<String, String> nameMapping =
|
||||
((fullFieldNames != null) && fullFieldNames.isPresent() && fullFieldNames.get())
|
||||
? getFieldAliases() : getFieldAliases().inverse();
|
||||
return ImmutableSet.copyOf(Iterables.transform(
|
||||
Iterables.concat(getPrimaryKeyFields(), fieldsToUse),
|
||||
new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String field) {
|
||||
// Rename fields that are in the map according to the map, and leave the others as is.
|
||||
return nameMapping.containsKey(field) ? nameMapping.get(field) : field;
|
||||
}}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a list of all available fields for use by the wildcard field specification.
|
||||
* Don't include aliases, since then we'd wind up returning the same field twice.
|
||||
*/
|
||||
private ImmutableList<String> getAllAvailableFields(ImmutableSet<T> objects) {
|
||||
ImmutableList.Builder<String> fields = new ImmutableList.Builder<>();
|
||||
for (T object : objects) {
|
||||
// Base case of the mapping is to use ImmutableObject's toDiffableFieldMap().
|
||||
fields.addAll(object.toDiffableFieldMap().keySet());
|
||||
// Next, overlay any field-level overrides specified by the subclass.
|
||||
fields.addAll(getFieldOverrides(object).keySet());
|
||||
}
|
||||
return fields.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a table of data for the given sets of fields and objects. The table is row-keyed by
|
||||
* object and column-keyed by field, in the same iteration order as the provided sets.
|
||||
*/
|
||||
private ImmutableTable<T, String, String>
|
||||
extractData(ImmutableSet<String> fields, ImmutableSet<T> objects) {
|
||||
ImmutableTable.Builder<T, String, String> builder = ImmutableTable.builder();
|
||||
for (T object : objects) {
|
||||
Map<String, Object> fieldMap = new HashMap<>();
|
||||
// Base case of the mapping is to use ImmutableObject's toDiffableFieldMap().
|
||||
fieldMap.putAll(object.toDiffableFieldMap());
|
||||
// Next, overlay any field-level overrides specified by the subclass.
|
||||
fieldMap.putAll(getFieldOverrides(object));
|
||||
// Next, add to the mapping all the aliases, with their values defined as whatever was in the
|
||||
// map under the aliased field's original name.
|
||||
fieldMap.putAll(
|
||||
Maps.transformValues(getFieldAliases(), Functions.forMap(new HashMap<>(fieldMap))));
|
||||
Set<String> expectedFields = ImmutableSortedSet.copyOf(fieldMap.keySet());
|
||||
for (String field : fields) {
|
||||
checkArgument(fieldMap.containsKey(field),
|
||||
"Field '%s' not found - recognized fields are:\n%s", field, expectedFields);
|
||||
builder.put(object, field, Objects.toString(fieldMap.get(field), ""));
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the column widths of the given table of strings column-keyed by strings and returns
|
||||
* them as a map from column key name to integer width. The column width is defined as the max
|
||||
* length of any string in that column, including the name of the column.
|
||||
*/
|
||||
private static ImmutableMap<String, Integer> computeColumnWidths(
|
||||
ImmutableTable<?, String, String> data, final boolean includingHeader) {
|
||||
return ImmutableMap.copyOf(Maps.transformEntries(
|
||||
data.columnMap(),
|
||||
new Maps.EntryTransformer<String, Map<?, String>, Integer>() {
|
||||
@Override
|
||||
public Integer transformEntry(String columnName, Map<?, String> columnValues) {
|
||||
// Return the length of the longest string in this column (including the column name).
|
||||
return Ordering.natural().max(Iterables.transform(
|
||||
Iterables.concat(
|
||||
ImmutableList.of(includingHeader ? columnName : ""),
|
||||
columnValues.values()),
|
||||
new Function<String, Integer>() {
|
||||
@Override
|
||||
public Integer apply(String value) {
|
||||
return value.length();
|
||||
}}));
|
||||
}}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether to display headers. If the parameter is not set, print headers only if there
|
||||
* is more than one column.
|
||||
*/
|
||||
private boolean isHeaderRowInUse(final ImmutableTable<?, String, String> data) {
|
||||
return ((printHeaderRow != null) && printHeaderRow.isPresent())
|
||||
? printHeaderRow.get() : (data.columnKeySet().size() > 1);
|
||||
}
|
||||
|
||||
/** Converts the provided table of data to text, formatted using the provided column widths. */
|
||||
private List<String> generateFormattedData(
|
||||
ImmutableTable<T, String, String> data,
|
||||
ImmutableMap<String, Integer> columnWidths) {
|
||||
Function<Map<String, String>, String> rowFormatter = makeRowFormatter(columnWidths);
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
if (isHeaderRowInUse(data)) {
|
||||
// Add a row of headers (column names mapping to themselves).
|
||||
Map<String, String> headerRow =
|
||||
Maps.asMap(data.columnKeySet(), Functions.<String>identity());
|
||||
lines.add(rowFormatter.apply(headerRow));
|
||||
|
||||
// Add a row of separator lines (column names mapping to '-' * column width).
|
||||
Map<String, String> separatorRow = Maps.transformValues(columnWidths,
|
||||
new Function<Integer, String>() {
|
||||
@Override
|
||||
public String apply(Integer width) {
|
||||
return Strings.repeat("-", width);
|
||||
}});
|
||||
lines.add(rowFormatter.apply(separatorRow));
|
||||
}
|
||||
|
||||
// Add the actual data rows.
|
||||
for (Map<String, String> row : data.rowMap().values()) {
|
||||
lines.add(rowFormatter.apply(row));
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns for the given column widths map a row formatting function that converts a row map (of
|
||||
* column keys to cell values) into a single string with each column right-padded to that width.
|
||||
* <p>
|
||||
* The resulting strings separate padded fields with two spaces and each end in a newline.
|
||||
*/
|
||||
private static Function<Map<String, String>, String> makeRowFormatter(
|
||||
final Map<String, Integer> columnWidths) {
|
||||
return new Function<Map<String, String>, String>() {
|
||||
@Override
|
||||
public String apply(Map<String, String> rowByColumns) {
|
||||
List<String> paddedFields = new ArrayList<>();
|
||||
for (Map.Entry<String, String> cell : rowByColumns.entrySet()) {
|
||||
paddedFields.add(Strings.padEnd(cell.getValue(), columnWidths.get(cell.getKey()), ' '));
|
||||
}
|
||||
return Joiner.on(" ").join(paddedFields);
|
||||
}};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.request.Action.Method.GET;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.registry.label.PremiumList;
|
||||
import com.google.domain.registry.request.Action;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** An action that lists premium lists, for use by the registry_tool list_premium_lists command. */
|
||||
@Action(path = ListPremiumListsAction.PATH, method = {GET, POST})
|
||||
public final class ListPremiumListsAction extends ListObjectsAction<PremiumList> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/premiumLists";
|
||||
|
||||
@Inject ListPremiumListsAction() {}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<PremiumList> loadObjects() {
|
||||
return ImmutableSet.copyOf(
|
||||
ofy().load().type(PremiumList.class).ancestor(getCrossTldKey()).list());
|
||||
}
|
||||
}
|
58
java/google/registry/tools/server/ListRegistrarsAction.java
Normal file
58
java/google/registry/tools/server/ListRegistrarsAction.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.request.Action.Method.GET;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.request.Action;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** An action that lists registrars, for use by the registry_tool list_registrars command. */
|
||||
@Action(path = ListRegistrarsAction.PATH, method = {GET, POST})
|
||||
public final class ListRegistrarsAction extends ListObjectsAction<Registrar> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/registrars";
|
||||
|
||||
@Inject ListRegistrarsAction() {}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("clientIdentifier");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<Registrar> loadObjects() {
|
||||
return ImmutableSet.copyOf(Registrar.loadAll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBiMap<String, String> getFieldAliases() {
|
||||
return ImmutableBiMap.of(
|
||||
"clientId", "clientIdentifier",
|
||||
"premiumNames", "blockPremiumNames");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableMap<String, String> getFieldOverrides(Registrar registrar) {
|
||||
return ImmutableMap.of(
|
||||
"blockPremiumNames", registrar.getBlockPremiumNames() ? "blocked" : "-");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.request.Action.Method.GET;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.registry.label.ReservedList;
|
||||
import com.google.domain.registry.request.Action;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** A that lists reserved lists, for use by the registry_tool list_reserved_lists command. */
|
||||
@Action(path = ListReservedListsAction.PATH, method = {GET, POST})
|
||||
public final class ListReservedListsAction extends ListObjectsAction<ReservedList> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/reservedLists";
|
||||
|
||||
@Inject ListReservedListsAction() {}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<ReservedList> loadObjects() {
|
||||
return ImmutableSet.copyOf(
|
||||
ofy().load().type(ReservedList.class).ancestor(getCrossTldKey()).list());
|
||||
}
|
||||
}
|
82
java/google/registry/tools/server/ListTldsAction.java
Normal file
82
java/google/registry/tools/server/ListTldsAction.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.model.registry.Registries.getTlds;
|
||||
import static com.google.domain.registry.request.Action.Method.GET;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
/** An action that lists top-level domains, for use by the registry_tool list_tlds command. */
|
||||
@Action(path = ListTldsAction.PATH, method = {GET, POST})
|
||||
public final class ListTldsAction extends ListObjectsAction<Registry> {
|
||||
|
||||
public static final String PATH = "/_dr/admin/list/tlds";
|
||||
|
||||
@Inject Clock clock;
|
||||
@Inject ListTldsAction() {}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<String> getPrimaryKeyFields() {
|
||||
return ImmutableSet.of("tldStr");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableSet<Registry> loadObjects() {
|
||||
return FluentIterable.from(getTlds())
|
||||
.transform(new Function<String, Registry>() {
|
||||
@Override
|
||||
public Registry apply(String tldString) {
|
||||
return Registry.get(tldString);
|
||||
}})
|
||||
.toSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBiMap<String, String> getFieldAliases() {
|
||||
return ImmutableBiMap.of(
|
||||
"TLD", "tldStr",
|
||||
"dns", "dnsPaused",
|
||||
"escrow", "escrowEnabled",
|
||||
"premiumPricing", "premiumPriceAckRequired");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableMap<String, String> getFieldOverrides(Registry registry) {
|
||||
final DateTime now = clock.nowUtc();
|
||||
return new ImmutableMap.Builder<String, String>()
|
||||
.put("dnsPaused", registry.getDnsPaused() ? "paused" : "-")
|
||||
.put("escrowEnabled", registry.getEscrowEnabled() ? "enabled" : "-")
|
||||
.put("premiumPriceAckRequired", registry.getPremiumPriceAckRequired() ? "ack req'd" : "-")
|
||||
.put("tldState", registry.isPdt(now) ? "PDT" : registry.getTldState(now).toString())
|
||||
.put("tldStateTransitions", registry.getTldStateTransitions().toString())
|
||||
.put("renewBillingCost", registry.getStandardRenewCost(now).toString())
|
||||
.put("renewBillingCostTransitions", registry.getRenewBillingCostTransitions().toString())
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.domain.registry.mapreduce.inputs.EppResourceInputs.createEntityInput;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.PipelineUtils.createJobPath;
|
||||
|
||||
import com.google.appengine.tools.mapreduce.Mapper;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.mapreduce.MapreduceAction;
|
||||
import com.google.domain.registry.mapreduce.MapreduceRunner;
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Response;
|
||||
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* A mapreduce that re-saves all EppResources without otherwise modifying them.
|
||||
*
|
||||
* <p>This is useful for completing data migrations on EppResource fields that are accomplished
|
||||
* with @OnSave or @OnLoad annotations, and also guarantees that all EppResources will get fresh
|
||||
* commit logs (for backup purposes).
|
||||
*/
|
||||
@Action(path = "/_dr/task/resaveAllEppResources")
|
||||
public class ResaveAllEppResourcesAction implements MapreduceAction {
|
||||
|
||||
@Inject MapreduceRunner mrRunner;
|
||||
@Inject Response response;
|
||||
@Inject ResaveAllEppResourcesAction() {}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void run() {
|
||||
response.sendJavaScriptRedirect(createJobPath(mrRunner
|
||||
.setJobName("Re-save all EPP resources")
|
||||
.setModuleName("backend")
|
||||
.runMapOnly(
|
||||
new ResaveAllEppResourcesActionMapper(),
|
||||
ImmutableList.of(createEntityInput(EppResource.class)))));
|
||||
}
|
||||
|
||||
/** Mapper to re-save all EPP resources. */
|
||||
public static class ResaveAllEppResourcesActionMapper extends Mapper<EppResource, Void, Void> {
|
||||
|
||||
private static final long serialVersionUID = -7721628665138087001L;
|
||||
public ResaveAllEppResourcesActionMapper() {}
|
||||
|
||||
@Override
|
||||
public final void map(final EppResource resource) {
|
||||
ofy().transact(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
ofy().save().entity(resource).now();
|
||||
}});
|
||||
getContext().incrementCounter(
|
||||
String.format("%s entities re-saved", resource.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
91
java/google/registry/tools/server/ToolsServerModule.java
Normal file
91
java/google/registry/tools/server/ToolsServerModule.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.domain.registry.request.RequestParameters.extractBooleanParameter;
|
||||
import static com.google.domain.registry.request.RequestParameters.extractOptionalParameter;
|
||||
import static com.google.domain.registry.request.RequestParameters.extractRequiredParameter;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Dagger module for the tools package.
|
||||
*/
|
||||
@Module
|
||||
public class ToolsServerModule {
|
||||
|
||||
@Provides
|
||||
@Parameter("clientId")
|
||||
static Optional<String> provideClientId(HttpServletRequest req) {
|
||||
return Optional.fromNullable(emptyToNull(req.getParameter(CreateGroupsAction.CLIENT_ID_PARAM)));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("fields")
|
||||
static Optional<String> provideFields(HttpServletRequest req) {
|
||||
return extractOptionalParameter(req, ListObjectsAction.FIELDS_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("fullFieldNames")
|
||||
static Optional<Boolean> provideFullFieldNames(HttpServletRequest req) {
|
||||
String s = emptyToNull(req.getParameter(ListObjectsAction.FULL_FIELD_NAMES_PARAM));
|
||||
return (s == null) ? Optional.<Boolean>absent() : Optional.of(Boolean.parseBoolean(s));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("inputData")
|
||||
static String provideInput(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, CreatePremiumListAction.INPUT_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("name")
|
||||
static String provideName(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, CreatePremiumListAction.NAME_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("override")
|
||||
static boolean provideOverride(HttpServletRequest req) {
|
||||
return extractBooleanParameter(req, CreatePremiumListAction.OVERRIDE_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("printHeaderRow")
|
||||
static Optional<Boolean> providePrintHeaderRow(HttpServletRequest req) {
|
||||
String s = emptyToNull(req.getParameter(ListObjectsAction.PRINT_HEADER_ROW_PARAM));
|
||||
return (s == null) ? Optional.<Boolean>absent() : Optional.of(Boolean.parseBoolean(s));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("tld")
|
||||
static String provideTld(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "tld");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("rawKeys")
|
||||
static String provideRawKeys(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "rawKeys");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.model.registry.label.PremiumList;
|
||||
import com.google.domain.registry.request.Action;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* An action that creates a premium list, for use by the registry_tool create_premium_list command.
|
||||
*/
|
||||
@Action(path = UpdatePremiumListAction.PATH, method = POST)
|
||||
public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {
|
||||
|
||||
public static final String PATH = "/_dr/admin/updatePremiumList";
|
||||
|
||||
@Inject UpdatePremiumListAction() {}
|
||||
|
||||
@Override
|
||||
protected void savePremiumList() {
|
||||
Optional<PremiumList> existingName = PremiumList.get(name);
|
||||
checkArgument(
|
||||
existingName.isPresent(),
|
||||
"Could not update premium list %s because it doesn't exist.",
|
||||
name);
|
||||
|
||||
logger.infofmt("Updating premium list for TLD %s", name);
|
||||
logger.infofmt("Got the following input data: %s", inputData);
|
||||
List<String> inputDataPreProcessed =
|
||||
Splitter.on('\n').omitEmptyStrings().splitToList(inputData);
|
||||
PremiumList premiumList = existingName.get().asBuilder()
|
||||
.setPremiumListMapFromLines(inputDataPreProcessed)
|
||||
.build();
|
||||
premiumList.saveAndUpdateEntries();
|
||||
|
||||
logger.infofmt("Updated premium list %s with entries %s",
|
||||
premiumList.getName(),
|
||||
premiumList.getPremiumListEntries());
|
||||
|
||||
String message = String.format(
|
||||
"Saved premium list %s with %d entries.\n",
|
||||
premiumList.getName(),
|
||||
premiumList.getPremiumListEntries().size());
|
||||
response.setPayload(ImmutableMap.of(
|
||||
"status", "success",
|
||||
"message", message));
|
||||
}
|
||||
}
|
348
java/google/registry/tools/server/VerifyOteAction.java
Normal file
348
java/google/registry/tools/server/VerifyOteAction.java
Normal file
|
@ -0,0 +1,348 @@
|
|||
// Copyright 2016 The Domain Registry 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 com.google.domain.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.collect.Maps.toMap;
|
||||
import static com.google.domain.registry.flows.EppXmlTransformer.unmarshal;
|
||||
import static com.google.domain.registry.flows.FlowRegistry.getFlowClass;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.CollectionUtils.isNullOrEmpty;
|
||||
import static com.google.domain.registry.util.DomainNameUtils.ACE_PREFIX;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.Flow;
|
||||
import com.google.domain.registry.flows.contact.ContactCreateFlow;
|
||||
import com.google.domain.registry.flows.contact.ContactDeleteFlow;
|
||||
import com.google.domain.registry.flows.contact.ContactTransferApproveFlow;
|
||||
import com.google.domain.registry.flows.contact.ContactTransferCancelFlow;
|
||||
import com.google.domain.registry.flows.contact.ContactTransferRejectFlow;
|
||||
import com.google.domain.registry.flows.contact.ContactTransferRequestFlow;
|
||||
import com.google.domain.registry.flows.contact.ContactUpdateFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainApplicationCreateFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainApplicationDeleteFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainApplicationUpdateFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainCreateFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainDeleteFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainRenewFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainRestoreRequestFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainTransferApproveFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainTransferCancelFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainTransferRejectFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainTransferRequestFlow;
|
||||
import com.google.domain.registry.flows.domain.DomainUpdateFlow;
|
||||
import com.google.domain.registry.flows.host.HostCreateFlow;
|
||||
import com.google.domain.registry.flows.host.HostDeleteFlow;
|
||||
import com.google.domain.registry.flows.host.HostUpdateFlow;
|
||||
import com.google.domain.registry.model.domain.DomainCommand;
|
||||
import com.google.domain.registry.model.domain.fee.FeeCreateExtension;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchCreateExtension;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsCreateExtension;
|
||||
import com.google.domain.registry.model.domain.secdns.SecDnsUpdateExtension;
|
||||
import com.google.domain.registry.model.eppinput.EppInput;
|
||||
import com.google.domain.registry.model.eppinput.EppInput.ResourceCommandWrapper;
|
||||
import com.google.domain.registry.model.host.HostCommand;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.JsonActionRunner;
|
||||
import com.google.domain.registry.request.JsonActionRunner.JsonAction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* A servlet that verifies a registrar's OTE status. Note that this is eventually consistent, so
|
||||
* OT&E commands that have been run just previously to verification may not be picked up yet.
|
||||
*/
|
||||
@Action(
|
||||
path = VerifyOteAction.PATH,
|
||||
method = Action.Method.POST,
|
||||
xsrfProtection = true,
|
||||
xsrfScope = "admin")
|
||||
public class VerifyOteAction implements Runnable, JsonAction {
|
||||
|
||||
public static final String PATH = "/_dr/admin/verifyOte";
|
||||
|
||||
@Inject JsonActionRunner jsonActionRunner;
|
||||
@Inject VerifyOteAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
jsonActionRunner.run(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> handleJsonRequest(Map<String, ?> json) {
|
||||
final boolean summarize = Boolean.parseBoolean((String) json.get("summarize"));
|
||||
return toMap(
|
||||
(List<String>) json.get("registrars"),
|
||||
new Function<String, Object>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public Object apply(@Nonnull String registrar) {
|
||||
return checkRegistrar(registrar, summarize);
|
||||
}});
|
||||
}
|
||||
|
||||
/** Checks whether the provided registrar has passed OT&E and returns relevant information. */
|
||||
private String checkRegistrar(String registrarName, boolean summarize) {
|
||||
HistoryEntryStats historyEntryStats =
|
||||
new HistoryEntryStats().recordRegistrarHistory(registrarName);
|
||||
List<String> failureMessages = historyEntryStats.findFailures();
|
||||
String passedFraction = String.format(
|
||||
"%2d/%2d", StatType.NUM_REQUIREMENTS - failureMessages.size(), StatType.NUM_REQUIREMENTS);
|
||||
String status = failureMessages.isEmpty() ? "PASS" : "FAIL";
|
||||
return summarize
|
||||
? String.format(
|
||||
"Num actions: %4d - Reqs passed: %s - Overall: %s",
|
||||
historyEntryStats.statCounts.size(),
|
||||
passedFraction,
|
||||
status)
|
||||
: String.format(
|
||||
"%s\n%s\nRequirements passed: %s\nOverall OT&E status: %s\n",
|
||||
historyEntryStats,
|
||||
Joiner.on('\n').join(failureMessages),
|
||||
passedFraction,
|
||||
status);
|
||||
}
|
||||
|
||||
private static final Predicate<EppInput> HAS_CLAIMS_NOTICE = new Predicate<EppInput>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull EppInput eppInput) {
|
||||
LaunchCreateExtension launchCreate =
|
||||
eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
return launchCreate != null && launchCreate.getNotice() != null;
|
||||
}};
|
||||
|
||||
private static final Predicate<EppInput> HAS_FEE = new Predicate<EppInput>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull EppInput eppInput) {
|
||||
return eppInput.getSingleExtension(FeeCreateExtension.class) != null;
|
||||
}};
|
||||
|
||||
private static final Predicate<EppInput> HAS_SEC_DNS = new Predicate<EppInput>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull EppInput eppInput) {
|
||||
return (eppInput.getSingleExtension(SecDnsCreateExtension.class) != null)
|
||||
|| (eppInput.getSingleExtension(SecDnsUpdateExtension.class) != null);
|
||||
}};
|
||||
|
||||
private static final Predicate<EppInput> IS_SUNRISE = new Predicate<EppInput>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull EppInput eppInput) {
|
||||
LaunchCreateExtension launchCreate =
|
||||
eppInput.getSingleExtension(LaunchCreateExtension.class);
|
||||
return launchCreate != null && !isNullOrEmpty(launchCreate.getSignedMarks());
|
||||
}};
|
||||
|
||||
private static final Predicate<EppInput> IS_IDN = new Predicate<EppInput>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull EppInput eppInput) {
|
||||
return ((DomainCommand.Create) ((ResourceCommandWrapper)
|
||||
eppInput.getCommandWrapper().getCommand()).getResourceCommand())
|
||||
.getFullyQualifiedDomainName().startsWith(ACE_PREFIX);
|
||||
}};
|
||||
|
||||
private static final Predicate<EppInput> IS_SUBORDINATE = new Predicate<EppInput>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull EppInput eppInput) {
|
||||
return !isNullOrEmpty(((HostCommand.Create) ((ResourceCommandWrapper)
|
||||
eppInput.getCommandWrapper().getCommand()).getResourceCommand())
|
||||
.getInetAddresses());
|
||||
}};
|
||||
|
||||
private static Predicate<EppInput> isFlow(final Class<? extends Flow> flowClass) {
|
||||
return new Predicate<EppInput>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull EppInput eppInput) {
|
||||
try {
|
||||
return flowClass.equals(getFlowClass(eppInput));
|
||||
} catch (EppException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/** Enum defining the distinct statistics (types of registrar actions) to record. */
|
||||
public enum StatType {
|
||||
CONTACT_CREATES(0, isFlow(ContactCreateFlow.class)),
|
||||
CONTACT_DELETES(0, isFlow(ContactDeleteFlow.class)),
|
||||
CONTACT_TRANSFER_APPROVES(0, isFlow(ContactTransferApproveFlow.class)),
|
||||
CONTACT_TRANSFER_CANCELS(0, isFlow(ContactTransferCancelFlow.class)),
|
||||
CONTACT_TRANSFER_REJECTS(0, isFlow(ContactTransferRejectFlow.class)),
|
||||
CONTACT_TRANSFER_REQUESTS(0, isFlow(ContactTransferRequestFlow.class)),
|
||||
CONTACT_UPDATES(0, isFlow(ContactUpdateFlow.class)),
|
||||
DOMAIN_APPLICATION_CREATES(0, isFlow(DomainApplicationCreateFlow.class)),
|
||||
DOMAIN_APPLICATION_CREATES_LANDRUSH(
|
||||
1, isFlow(DomainApplicationCreateFlow.class), not(IS_SUNRISE)),
|
||||
DOMAIN_APPLICATION_CREATES_SUNRISE(1, isFlow(DomainApplicationCreateFlow.class), IS_SUNRISE),
|
||||
DOMAIN_APPLICATION_DELETES(2, isFlow(DomainApplicationDeleteFlow.class)),
|
||||
DOMAIN_APPLICATION_UPDATES(2, isFlow(DomainApplicationUpdateFlow.class)),
|
||||
DOMAIN_CREATES(0, isFlow(DomainCreateFlow.class)),
|
||||
DOMAIN_CREATES_ASCII(1, isFlow(DomainCreateFlow.class), not(IS_IDN)),
|
||||
DOMAIN_CREATES_IDN(1, isFlow(DomainCreateFlow.class), IS_IDN),
|
||||
DOMAIN_CREATES_WITH_CLAIMS_NOTICE(1, isFlow(DomainCreateFlow.class), HAS_CLAIMS_NOTICE),
|
||||
DOMAIN_CREATES_WITH_FEE(1, isFlow(DomainCreateFlow.class), HAS_FEE),
|
||||
DOMAIN_CREATES_WITH_SEC_DNS(1, isFlow(DomainCreateFlow.class), HAS_SEC_DNS),
|
||||
DOMAIN_CREATES_WITHOUT_SEC_DNS(0, isFlow(DomainCreateFlow.class), not(HAS_SEC_DNS)),
|
||||
DOMAIN_DELETES(2, isFlow(DomainDeleteFlow.class)),
|
||||
DOMAIN_RENEWS(0, isFlow(DomainRenewFlow.class)),
|
||||
DOMAIN_RESTORES(1, isFlow(DomainRestoreRequestFlow.class)),
|
||||
DOMAIN_TRANSFER_APPROVES(1, isFlow(DomainTransferApproveFlow.class)),
|
||||
DOMAIN_TRANSFER_CANCELS(1, isFlow(DomainTransferCancelFlow.class)),
|
||||
DOMAIN_TRANSFER_REJECTS(1, isFlow(DomainTransferRejectFlow.class)),
|
||||
DOMAIN_TRANSFER_REQUESTS(1, isFlow(DomainTransferRequestFlow.class)),
|
||||
DOMAIN_UPDATES(0, isFlow(DomainUpdateFlow.class)),
|
||||
DOMAIN_UPDATES_WITH_SEC_DNS(1, isFlow(DomainUpdateFlow.class), HAS_SEC_DNS),
|
||||
DOMAIN_UPDATES_WITHOUT_SEC_DNS(0, isFlow(DomainUpdateFlow.class), not(HAS_SEC_DNS)),
|
||||
HOST_CREATES(0, isFlow(HostCreateFlow.class)),
|
||||
HOST_CREATES_EXTERNAL(0, isFlow(HostCreateFlow.class), not(IS_SUBORDINATE)),
|
||||
HOST_CREATES_SUBORDINATE(1, isFlow(HostCreateFlow.class), IS_SUBORDINATE),
|
||||
HOST_DELETES(1, isFlow(HostDeleteFlow.class)),
|
||||
HOST_UPDATES(1, isFlow(HostUpdateFlow.class)),
|
||||
UNCLASSIFIED_FLOWS(0, Predicates.<EppInput>alwaysFalse());
|
||||
|
||||
/** The number of StatTypes with a non-zero requirement. */
|
||||
private static final int NUM_REQUIREMENTS = FluentIterable.from(asList(values()))
|
||||
.filter(new Predicate<StatType>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull StatType statType) {
|
||||
return statType.requirement > 0;
|
||||
}})
|
||||
.size();
|
||||
|
||||
/** Required number of times registrars must complete this action. */
|
||||
final int requirement;
|
||||
|
||||
/** Filters to determine if this action was performed by an EppInput. */
|
||||
private Predicate<EppInput>[] filters;
|
||||
|
||||
@SafeVarargs
|
||||
StatType(int requirement, Predicate<EppInput>... filters) {
|
||||
this.requirement = requirement;
|
||||
this.filters = filters;
|
||||
}
|
||||
|
||||
/** Returns a more human-readable translation of the enum constant. */
|
||||
String description() {
|
||||
return this.name().replace('_', ' ').toLowerCase();
|
||||
}
|
||||
|
||||
/** An {@link EppInput} might match multiple actions, so check if this action matches. */
|
||||
boolean matches(EppInput eppInput) {
|
||||
return Predicates.and(filters).apply(eppInput);
|
||||
}
|
||||
}
|
||||
|
||||
/** Class to represent stats derived from HistoryEntry objects on actions taken by registrars. */
|
||||
static class HistoryEntryStats {
|
||||
|
||||
/** Stores counts of how many times each action type was performed. */
|
||||
Multiset<StatType> statCounts = HashMultiset.create();
|
||||
|
||||
/**
|
||||
* Records data in the passed historyEntryStats object on what actions have been performed by
|
||||
* the four numbered OT&E variants of the registrar name.
|
||||
*/
|
||||
HistoryEntryStats recordRegistrarHistory(String registrarName) {
|
||||
ImmutableList.Builder<String> clientIds = new ImmutableList.Builder<>();
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
clientIds.add(String.format("%s-%d", registrarName, i));
|
||||
}
|
||||
for (HistoryEntry historyEntry :
|
||||
ofy().load().type(HistoryEntry.class).filter("clientId in", clientIds.build()).list()) {
|
||||
try {
|
||||
record(historyEntry);
|
||||
} catch (EppException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Interprets the data in the provided HistoryEntry and increments counters. */
|
||||
void record(HistoryEntry historyEntry) throws EppException {
|
||||
byte[] xmlBytes = historyEntry.getXmlBytes();
|
||||
// xmlBytes can be null on contact create and update for safe-harbor compliance.
|
||||
//
|
||||
// TODO(b/26161587): inspect the history entry itself to handle this properly.
|
||||
if (xmlBytes == null) {
|
||||
return;
|
||||
}
|
||||
final EppInput eppInput = unmarshal(xmlBytes);
|
||||
if (!statCounts.addAll(
|
||||
FluentIterable.from(EnumSet.allOf(StatType.class))
|
||||
.filter(
|
||||
new Predicate<StatType>() {
|
||||
@Override
|
||||
public boolean apply(@Nonnull StatType statType) {
|
||||
return statType.matches(eppInput);
|
||||
}
|
||||
})
|
||||
.toList())) {
|
||||
statCounts.add(StatType.UNCLASSIFIED_FLOWS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of failure messages describing any cases where the passed stats fail to
|
||||
* meet the required thresholds, or the empty list if all requirements are met.
|
||||
*/
|
||||
List<String> findFailures() {
|
||||
List<String> messages = new ArrayList<>();
|
||||
for (StatType statType : StatType.values()) {
|
||||
if (statCounts.count(statType) < statType.requirement) {
|
||||
messages.add(String.format(
|
||||
"Failure: %s %s found.",
|
||||
(statType.requirement == 1 ? "No" : "Not enough"),
|
||||
statType.description()));
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
/** Returns a string showing all possible actions and how many times each was performed. */
|
||||
@Override
|
||||
public String toString() {
|
||||
return FluentIterable.from(EnumSet.allOf(StatType.class))
|
||||
.transform(
|
||||
new Function<StatType, String>() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public String apply(@Nonnull StatType statType) {
|
||||
return String.format(
|
||||
"%s: %d", statType.description(), statCounts.count(statType));
|
||||
}
|
||||
})
|
||||
.append(String.format("TOTAL: %d", statCounts.size()))
|
||||
.join(Joiner.on("\n"));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue