mirror of
https://github.com/google/nomulus.git
synced 2025-05-19 02:39:34 +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
187
java/google/registry/tools/AllocateDomainCommand.java
Normal file
187
java/google/registry/tools/AllocateDomainCommand.java
Normal file
|
@ -0,0 +1,187 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.domain.registry.flows.EppXmlTransformer.unmarshal;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.tools.CommandUtilities.addHeader;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.EppXmlTransformer;
|
||||
import com.google.domain.registry.model.domain.DesignatedContact;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.DomainCommand;
|
||||
import com.google.domain.registry.model.domain.Period;
|
||||
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchNotice;
|
||||
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
|
||||
import com.google.domain.registry.model.eppinput.EppInput;
|
||||
import com.google.domain.registry.model.eppinput.EppInput.ResourceCommandWrapper;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.model.smd.SignedMark;
|
||||
import com.google.domain.registry.tools.soy.DomainAllocateSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
import com.googlecode.objectify.Work;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to allocated a domain from a domain application. */
|
||||
@Parameters(separators = " =", commandDescription = "Allocate a domain application")
|
||||
final class AllocateDomainCommand extends MutatingEppToolCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--ids",
|
||||
description = "Comma-delimited list of application IDs to update.",
|
||||
required = true)
|
||||
String ids;
|
||||
|
||||
private final List<Key<DomainApplication>> applicationKeys = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected String postExecute() throws Exception {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
// Check to see that we allocated everything.
|
||||
return builder.append(ofy().transactNewReadOnly(new Work<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
String failureMessage = FluentIterable
|
||||
.from(ofy().load().keys(applicationKeys).values())
|
||||
.transform(new Function<DomainApplication, String>() {
|
||||
@Override
|
||||
public String apply(DomainApplication application) {
|
||||
return application.getApplicationStatus() == ApplicationStatus.ALLOCATED
|
||||
? null : application.getFullyQualifiedDomainName();
|
||||
}})
|
||||
.join(Joiner.on('\n').skipNulls());
|
||||
return failureMessage.isEmpty() ? "ALL SUCCEEDED" : addHeader("FAILURES", failureMessage);
|
||||
}})).toString();
|
||||
}
|
||||
|
||||
/** Extract the registration period from the XML used to create the domain application. */
|
||||
private static Period extractPeriodFromXml(byte[] xmlBytes) throws EppException {
|
||||
EppInput eppInput = unmarshal(xmlBytes);
|
||||
return ((DomainCommand.Create)
|
||||
((ResourceCommandWrapper) eppInput.getCommandWrapper().getCommand())
|
||||
.getResourceCommand()).getPeriod();
|
||||
}
|
||||
|
||||
@Override
|
||||
void initMutatingEppToolCommand() {
|
||||
checkArgument(superuser, "This command MUST be run as --superuser.");
|
||||
setSoyTemplate(DomainAllocateSoyInfo.getInstance(), DomainAllocateSoyInfo.CREATE);
|
||||
ofy().transactNewReadOnly(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
Iterable<Key<DomainApplication>> keys = transform(
|
||||
Splitter.on(',').split(ids),
|
||||
new Function<String, Key<DomainApplication>>() {
|
||||
@Override
|
||||
public Key<DomainApplication> apply(String applicationId) {
|
||||
return Key.create(DomainApplication.class, applicationId);
|
||||
}});
|
||||
for (DomainApplication application : ofy().load().keys(keys).values()) {
|
||||
// If the application is already allocated print a warning but do not fail.
|
||||
if (application.getApplicationStatus() == ApplicationStatus.ALLOCATED) {
|
||||
System.err.printf(
|
||||
"Application %s has already been allocated\n", application.getRepoId());
|
||||
continue;
|
||||
}
|
||||
// Ensure domain doesn't already have a final status which it shouldn't be updated from.
|
||||
checkState(
|
||||
!application.getApplicationStatus().isFinalStatus(),
|
||||
"Application has final status %s",
|
||||
application.getApplicationStatus());
|
||||
try {
|
||||
HistoryEntry history = checkNotNull(
|
||||
ofy().load()
|
||||
.type(HistoryEntry.class)
|
||||
.ancestor(checkNotNull(application))
|
||||
.order("modificationTime")
|
||||
.first()
|
||||
.now(),
|
||||
"Could not find any history entries for domain application %s",
|
||||
application.getRepoId());
|
||||
String clientTransactionId =
|
||||
emptyToNull(history.getTrid().getClientTransactionId());
|
||||
Period period = checkNotNull(extractPeriodFromXml(history.getXmlBytes()));
|
||||
checkArgument(period.getUnit() == Period.Unit.YEARS);
|
||||
ImmutableMap.Builder<String, String> contactsMapBuilder = new ImmutableMap.Builder<>();
|
||||
for (DesignatedContact contact : application.getContacts()) {
|
||||
contactsMapBuilder.put(
|
||||
contact.getType().toString().toLowerCase(),
|
||||
contact.getContactId().getLinked().get().getForeignKey());
|
||||
}
|
||||
LaunchNotice launchNotice = application.getLaunchNotice();
|
||||
addSoyRecord(application.getCurrentSponsorClientId(), new SoyMapData(
|
||||
"name", application.getFullyQualifiedDomainName(),
|
||||
"period", period.getValue(),
|
||||
"nameservers", FluentIterable.from(application.loadNameservers())
|
||||
.transform(new Function<HostResource, String>() {
|
||||
@Override
|
||||
public String apply(HostResource host) {
|
||||
return host.getForeignKey();
|
||||
}})
|
||||
.toList(),
|
||||
"registrant", application.loadRegistrant().getForeignKey(),
|
||||
"contacts", contactsMapBuilder.build(),
|
||||
"authInfo", application.getAuthInfo().getPw().getValue(),
|
||||
"smdId", application.getEncodedSignedMarks().isEmpty()
|
||||
? null : EppXmlTransformer.<SignedMark>unmarshal(
|
||||
application.getEncodedSignedMarks().get(0).getBytes()).getId(),
|
||||
"applicationRoid", application.getRepoId(),
|
||||
"applicationTime", application.getCreationTime().toString(),
|
||||
"launchNotice", launchNotice == null ? null : ImmutableMap.of(
|
||||
"noticeId", launchNotice.getNoticeId().getTcnId(),
|
||||
"expirationTime", launchNotice.getExpirationTime().toString(),
|
||||
"acceptedTime", launchNotice.getAcceptedTime().toString()),
|
||||
"dsRecords", FluentIterable.from(application.getDsData())
|
||||
.transform(new Function<DelegationSignerData, ImmutableMap<String, ?>>() {
|
||||
@Override
|
||||
public ImmutableMap<String, ?> apply(DelegationSignerData dsData) {
|
||||
return ImmutableMap.of(
|
||||
"keyTag", dsData.getKeyTag(),
|
||||
"algorithm", dsData.getAlgorithm(),
|
||||
"digestType", dsData.getDigestType(),
|
||||
"digest", base16().encode(dsData.getDigest()));
|
||||
}})
|
||||
.toList(),
|
||||
"clTrid", clientTransactionId));
|
||||
applicationKeys.add(Key.create(application));
|
||||
} catch (EppException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
179
java/google/registry/tools/AppEngineConnection.java
Normal file
179
java/google/registry/tools/AppEngineConnection.java
Normal file
|
@ -0,0 +1,179 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Suppliers.memoize;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static com.google.common.net.HttpHeaders.X_REQUESTED_WITH;
|
||||
import static com.google.common.net.MediaType.JSON_UTF_8;
|
||||
import static com.google.domain.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
||||
import static com.google.domain.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
import com.google.domain.registry.security.XsrfTokenManager;
|
||||
import com.google.domain.registry.tools.ServerSideCommand.Connection;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** An http connection to the appengine server. */
|
||||
@Parameters(separators = " =")
|
||||
class AppEngineConnection implements Connection {
|
||||
|
||||
/** Pattern to heuristically extract title tag contents in HTML responses. */
|
||||
private static final Pattern HTML_TITLE_TAG_PATTERN = Pattern.compile("<title>(.*?)</title>");
|
||||
|
||||
|
||||
@Parameter(
|
||||
names = "--server",
|
||||
description = "HOST[:PORT] to which remote commands are sent.")
|
||||
private HostAndPort server = RegistryEnvironment.get().config().getServer();
|
||||
|
||||
@Parameter(
|
||||
names = "--remote_server_spec",
|
||||
description = "Combined server spec for the backend to connect to for remote logging.")
|
||||
private String remoteServerSpec = "gslb:apphosting-frontend:4";
|
||||
|
||||
@Parameter(
|
||||
names = "--remote_connection_timeout",
|
||||
description = "How long to wait for the remote logger server before giving up.")
|
||||
private Duration remoteConnectionTimeout = Duration.standardSeconds(30);
|
||||
|
||||
/**
|
||||
* Memoized XSRF security token.
|
||||
*
|
||||
* <p>Computing this is expensive since it needs to load {@code ServerSecret} so do it once.
|
||||
*/
|
||||
private final Supplier<String> xsrfToken =
|
||||
memoize(new Supplier<String>() {
|
||||
@Override
|
||||
public String get() {
|
||||
return XsrfTokenManager.generateToken("admin", getUserId());
|
||||
}});
|
||||
|
||||
@Override
|
||||
public void prefetchXsrfToken() throws IOException {
|
||||
// Cause XSRF token to be fetched, and then stay resident in cache (since it's memoized).
|
||||
xsrfToken.get();
|
||||
}
|
||||
|
||||
/** Returns the contents of the title tag in the given HTML, or null if not found. */
|
||||
private static String extractHtmlTitle(String html) {
|
||||
Matcher matcher = HTML_TITLE_TAG_PATTERN.matcher(html);
|
||||
return (matcher.find() ? matcher.group(1) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String send(
|
||||
String endpoint, Map<String, ?> params, MediaType contentType, byte[] payload)
|
||||
throws IOException {
|
||||
HttpURLConnection connection = getHttpURLConnection(
|
||||
new URL(String.format("http://%s%s?%s", getServer(), endpoint, encodeParams(params))));
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setUseCaches(false);
|
||||
connection.setRequestProperty(CONTENT_TYPE, contentType.toString());
|
||||
connection.setRequestProperty(X_CSRF_TOKEN, xsrfToken.get());
|
||||
connection.setRequestProperty(X_REQUESTED_WITH, "RegistryTool");
|
||||
connection.setDoOutput(true);
|
||||
connection.connect();
|
||||
try (OutputStream output = connection.getOutputStream()) {
|
||||
output.write(payload);
|
||||
}
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||
String errorTitle = extractHtmlTitle(
|
||||
CharStreams.toString(new InputStreamReader(connection.getErrorStream(), UTF_8)));
|
||||
throw new IOException(String.format(
|
||||
"Error from %s: %d %s%s",
|
||||
connection.getURL(),
|
||||
connection.getResponseCode(),
|
||||
connection.getResponseMessage(),
|
||||
(errorTitle == null ? "" : ": " + errorTitle)));
|
||||
}
|
||||
return CharStreams.toString(new InputStreamReader(connection.getInputStream(), UTF_8));
|
||||
}
|
||||
|
||||
private String encodeParams(Map<String, ?> params) {
|
||||
return Joiner.on('&').join(Iterables.transform(
|
||||
params.entrySet(),
|
||||
new Function<Entry<String, ?>, String>() {
|
||||
@Override
|
||||
public String apply(Entry<String, ?> entry) {
|
||||
try {
|
||||
return entry.getKey()
|
||||
+ "=" + URLEncoder.encode(entry.getValue().toString(), UTF_8.name());
|
||||
} catch (Exception e) { // UnsupportedEncodingException
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> sendJson(String endpoint, Map<String, ?> object) throws IOException {
|
||||
String response = send(
|
||||
endpoint,
|
||||
ImmutableMap.<String, Object>of(),
|
||||
JSON_UTF_8,
|
||||
JSONValue.toJSONString(object).getBytes(UTF_8));
|
||||
return (Map<String, Object>) JSONValue.parse(response.substring(JSON_SAFETY_PREFIX.length()));
|
||||
}
|
||||
|
||||
private HttpURLConnection getHttpURLConnection(URL remoteUrl) throws IOException {
|
||||
// TODO: Figure out authentication.
|
||||
return (HttpURLConnection) remoteUrl.openConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerUrl() {
|
||||
return "https://" + getServer().toString().replaceFirst("\\.", "-dot-");
|
||||
}
|
||||
|
||||
HostAndPort getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
boolean isLocalhost() {
|
||||
return server.getHostText().equals("localhost");
|
||||
}
|
||||
|
||||
private String getUserId() {
|
||||
return isLocalhost()
|
||||
? UserIdProvider.getTestUserId()
|
||||
: UserIdProvider.getProdUserId();
|
||||
}
|
||||
}
|
121
java/google/registry/tools/AuctionStatusCommand.java
Normal file
121
java/google/registry/tools/AuctionStatusCommand.java
Normal file
|
@ -0,0 +1,121 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.domain.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.findTldForName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.model.contact.ContactResource;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Work;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to check the status of domain applications. */
|
||||
@Parameters(separators = " =", commandDescription = "Check auction status")
|
||||
final class AuctionStatusCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Domains(s) to check",
|
||||
required = true)
|
||||
private List<String> mainArguments;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file.",
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
final ImmutableSet<String> domains = ImmutableSet.copyOf(mainArguments);
|
||||
Files.write(output, FluentIterable
|
||||
.from(domains)
|
||||
.transformAndConcat(new Function<String, Iterable<String>>() {
|
||||
@Override
|
||||
public Iterable<String> apply(String fullyQualifiedDomainName) {
|
||||
checkState(
|
||||
findTldForName(InternetDomainName.from(fullyQualifiedDomainName)).isPresent(),
|
||||
"No tld found for %s", fullyQualifiedDomainName);
|
||||
return ofy().transactNewReadOnly(new Work<Iterable<String>>() {
|
||||
@Override
|
||||
public Iterable<String> run() {
|
||||
ImmutableList.Builder<DomainApplication> applications =
|
||||
new ImmutableList.Builder<>();
|
||||
for (String domain : domains) {
|
||||
applications.addAll(
|
||||
loadActiveApplicationsByDomainName(domain, ofy().getTransactionTime()));
|
||||
}
|
||||
return Lists.transform(
|
||||
FluentIterable.from(applications.build()).toSortedList(ORDERING),
|
||||
APPLICATION_FORMATTER);
|
||||
}});
|
||||
}}), UTF_8);
|
||||
}
|
||||
|
||||
private static final Ordering<DomainApplication> ORDERING = new Ordering<DomainApplication>() {
|
||||
@Override
|
||||
public int compare(DomainApplication left, DomainApplication right) {
|
||||
return ComparisonChain.start()
|
||||
.compare(left.getFullyQualifiedDomainName(), right.getFullyQualifiedDomainName())
|
||||
.compareTrueFirst(
|
||||
left.getEncodedSignedMarks().isEmpty(), right.getEncodedSignedMarks().isEmpty())
|
||||
.compare(left.getApplicationStatus(), right.getApplicationStatus())
|
||||
.compare(left.getCreationTime(), right.getCreationTime())
|
||||
.result();
|
||||
}};
|
||||
|
||||
private static final Function<DomainApplication, String> APPLICATION_FORMATTER =
|
||||
new Function<DomainApplication, String>() {
|
||||
@Override
|
||||
public String apply(DomainApplication app) {
|
||||
ContactResource registrant = checkNotNull(app.loadRegistrant());
|
||||
Object[] keysAndValues = new Object[] {
|
||||
"Domain", app.getFullyQualifiedDomainName(),
|
||||
"Type", app.getEncodedSignedMarks().isEmpty() ? "Landrush" : "Sunrise",
|
||||
"Application Status", app.getApplicationStatus(),
|
||||
"Application ID", app.getForeignKey(),
|
||||
"Application Timestamp", app.getCreationTime(),
|
||||
"Last Update", app.getLastEppUpdateTime(),
|
||||
"Registrar Name", app.getCurrentSponsorClientId(),
|
||||
"Registrant Email", registrant.getEmailAddress(),
|
||||
"Registrant Phone", registrant.getVoiceNumber().getPhoneNumber()
|
||||
};
|
||||
return String.format(
|
||||
Strings.repeat("%-25s= %s\n", keysAndValues.length / 2), keysAndValues);
|
||||
}};
|
||||
}
|
91
java/google/registry/tools/BUILD
Normal file
91
java/google/registry/tools/BUILD
Normal file
|
@ -0,0 +1,91 @@
|
|||
package(
|
||||
default_visibility = ["//java/com/google/domain/registry:registry_project"],
|
||||
)
|
||||
|
||||
|
||||
# Restrict visibility to :tools because :remoteapi-internal (and transitively
|
||||
# :appengine-api-link) should never be linked into an App Engine deploy jar,
|
||||
# since the App Engine API is provided by the runtime environment.
|
||||
package_group(
|
||||
name = "allowed-tools",
|
||||
packages = [
|
||||
"//java/com/google/domain/registry/testing",
|
||||
"//java/com/google/domain/registry/tools",
|
||||
"//java/com/google/domain/registry/eclipse",
|
||||
"//javatests/com/google/domain/registry/tools",
|
||||
],
|
||||
)
|
||||
|
||||
java_library(
|
||||
name = "tools",
|
||||
srcs = glob([
|
||||
"*.java",
|
||||
"javascrap/*.java",
|
||||
]),
|
||||
resources = glob([
|
||||
"*.properties",
|
||||
"sql/*.sql",
|
||||
]),
|
||||
visibility = [":allowed-tools"],
|
||||
deps = [
|
||||
"//apiserving/discoverydata/bigquery:bigqueryv2",
|
||||
"//java/com/google/api/client/googleapis/auth/oauth2",
|
||||
"//java/com/google/api/client/http",
|
||||
"//java/com/google/api/client/http/javanet",
|
||||
"//java/com/google/api/client/json",
|
||||
"//java/com/google/api/client/json/jackson2",
|
||||
"//java/com/google/common/annotations",
|
||||
"//java/com/google/common/base",
|
||||
"//java/com/google/common/collect",
|
||||
"//java/com/google/common/hash",
|
||||
"//java/com/google/common/io",
|
||||
"//java/com/google/common/net",
|
||||
"//java/com/google/common/util/concurrent",
|
||||
"//java/com/google/domain/registry/bigquery",
|
||||
"//java/com/google/domain/registry/config",
|
||||
"//java/com/google/domain/registry/export",
|
||||
"//java/com/google/domain/registry/flows",
|
||||
"//java/com/google/domain/registry/keyring/api",
|
||||
"//java/com/google/domain/registry/model",
|
||||
"//java/com/google/domain/registry/rde",
|
||||
"//java/com/google/domain/registry/security",
|
||||
"//java/com/google/domain/registry/request:modules",
|
||||
"//java/com/google/domain/registry/tldconfig/idn",
|
||||
"//java/com/google/domain/registry/tmch",
|
||||
"//java/com/google/domain/registry/tools/params",
|
||||
"//java/com/google/domain/registry/tools/server",
|
||||
"//java/com/google/domain/registry/tools/soy:soy_java_wrappers",
|
||||
"//java/com/google/domain/registry/util",
|
||||
"//java/com/google/domain/registry/whois",
|
||||
"//java/com/google/domain/registry/xjc",
|
||||
"//java/com/google/domain/registry/xml",
|
||||
"//third_party/java/appengine:appengine-api",
|
||||
"//third_party/java/bouncycastle",
|
||||
"//third_party/java/bouncycastle_bcpg",
|
||||
"//third_party/java/dagger",
|
||||
"//third_party/java/jcommander",
|
||||
"//third_party/java/joda_money",
|
||||
"//third_party/java/joda_time",
|
||||
"//third_party/java/json",
|
||||
"//third_party/java/json_simple",
|
||||
"//third_party/java/jsr305_annotations",
|
||||
"//third_party/java/jsr330_inject",
|
||||
"//third_party/java/objectify:objectify-v4_1",
|
||||
|
||||
"//third_party/java/appengine:appengine-remote-api",
|
||||
|
||||
"//third_party/closure/templates",
|
||||
],
|
||||
)
|
||||
|
||||
java_binary(
|
||||
name = "registry_tool",
|
||||
create_executable = 1,
|
||||
main_class = "com.google.domain.registry.tools.RegistryTool",
|
||||
runtime_deps = [
|
||||
":tools",
|
||||
"//third_party/java/appengine:appengine-api-link",
|
||||
"//third_party/java/appengine:appengine-remote-api-link",
|
||||
],
|
||||
)
|
||||
|
46
java/google/registry/tools/BigqueryCommand.java
Normal file
46
java/google/registry/tools/BigqueryCommand.java
Normal file
|
@ -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;
|
||||
|
||||
import com.google.domain.registry.bigquery.BigqueryConnection;
|
||||
|
||||
import com.beust.jcommander.ParametersDelegate;
|
||||
|
||||
/** A {@link Command} that uses the bigquery client API. */
|
||||
abstract class BigqueryCommand implements Command {
|
||||
|
||||
/** Parameter delegate for encapsulating flags needed to set up the {@link BigqueryConnection}. */
|
||||
@ParametersDelegate
|
||||
private BigqueryParameters bigqueryParameters = new BigqueryParameters();
|
||||
|
||||
/** Connection object for interacting with the Bigquery API. */
|
||||
private BigqueryConnection bigquery;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
try (BigqueryConnection autoClosingBigquery = bigqueryParameters.newConnection()) {
|
||||
bigquery = autoClosingBigquery;
|
||||
runWithBigquery();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the {@link BigqueryConnection} object that has been initialized for use. */
|
||||
BigqueryConnection bigquery() {
|
||||
return bigquery;
|
||||
}
|
||||
|
||||
/** Subclasses must override this to define command behavior. */
|
||||
abstract void runWithBigquery() throws Exception;
|
||||
}
|
54
java/google/registry/tools/BigqueryCommandUtilities.java
Normal file
54
java/google/registry/tools/BigqueryCommandUtilities.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.domain.registry.bigquery.BigqueryConnection.DestinationTable;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/** Container class for static utility methods for Bigquery commands. */
|
||||
final class BigqueryCommandUtilities {
|
||||
|
||||
/**
|
||||
* Handler that takes a DestinationTable future and waits on its completion, printing generic
|
||||
* success/failure messages and wrapping any exception thrown in a TableCreationException.
|
||||
*/
|
||||
static void handleTableCreation(
|
||||
String tableDescription,
|
||||
ListenableFuture<DestinationTable> tableFuture) throws TableCreationException {
|
||||
System.err.printf("Creating %s...\n", tableDescription);
|
||||
try {
|
||||
DestinationTable table = tableFuture.get();
|
||||
System.err.printf(" - Success: created %s.\n", table.getStringReference());
|
||||
} catch (Exception e) {
|
||||
Throwable error = e;
|
||||
if (e instanceof ExecutionException) {
|
||||
error = e.getCause();
|
||||
}
|
||||
String errorMessage =
|
||||
String.format("Failed to create %s: %s", tableDescription, error.getMessage());
|
||||
System.err.printf(" - %s\n", errorMessage);
|
||||
throw new TableCreationException(errorMessage, error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception thrown if any error occurs during a table creation stage. */
|
||||
static class TableCreationException extends Exception {
|
||||
TableCreationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
103
java/google/registry/tools/BigqueryParameters.java
Normal file
103
java/google/registry/tools/BigqueryParameters.java
Normal file
|
@ -0,0 +1,103 @@
|
|||
// 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;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.JsonFactory;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
import com.google.api.services.bigquery.BigqueryScopes;
|
||||
import com.google.domain.registry.bigquery.BigqueryConnection;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/** Parameter delegate class to handle flag settings for a command's BigqueryConnection object. */
|
||||
@Parameters(separators = " =")
|
||||
final class BigqueryParameters {
|
||||
|
||||
/**
|
||||
* Default to 20 threads to stay within Bigquery's rate limit of 20 concurrent queries.
|
||||
*
|
||||
* @see "https://cloud.google.com/bigquery/quota-policy"
|
||||
*/
|
||||
private static final int DEFAULT_NUM_THREADS = 20;
|
||||
|
||||
@Parameter(
|
||||
names = "--bigquery_service_account",
|
||||
description = "Email for the Google APIs service account to use.")
|
||||
private String bigqueryServiceAccountEmail =
|
||||
"1080941367941-ic4pknfqcj1q7hhc9ob0bls920v80unu@developer.gserviceaccount.com";
|
||||
|
||||
@Parameter(
|
||||
names = "--bigquery_service_account_key",
|
||||
description = "PKCS file (.p12) containing the private key for the service account.",
|
||||
validateWith = PathParameter.InputFile.class)
|
||||
private Path bigqueryServiceAccountKeyFile = Paths.get("key.p12");
|
||||
|
||||
@Parameter(
|
||||
names = "--bigquery_dataset",
|
||||
description = "Name of the default dataset to use, for reading and writing.")
|
||||
private String bigqueryDataset = BigqueryConnection.DEFAULT_DATASET_NAME;
|
||||
|
||||
@Parameter(
|
||||
names = "--bigquery_overwrite",
|
||||
description = "Whether to automatically overwrite existing tables and views.")
|
||||
private boolean bigqueryOverwrite;
|
||||
|
||||
@Parameter(
|
||||
names = "--bigquery_poll_interval",
|
||||
description = "Interval in milliseconds to wait between polls for job status.")
|
||||
private Duration bigqueryPollInterval = Duration.standardSeconds(1);
|
||||
|
||||
@Parameter(
|
||||
names = "--bigquery_num_threads",
|
||||
description = "Number of threads for running simultaneous BigQuery operations.")
|
||||
private int bigqueryNumThreads = DEFAULT_NUM_THREADS;
|
||||
|
||||
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
|
||||
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
|
||||
|
||||
/** Returns a new BigqueryConnection constructed according to the delegate's flag settings. */
|
||||
BigqueryConnection newConnection() throws Exception {
|
||||
BigqueryConnection connection = new BigqueryConnection.Builder()
|
||||
.setExecutorService(Executors.newFixedThreadPool(bigqueryNumThreads))
|
||||
.setCredential(newCredential())
|
||||
.setDatasetId(bigqueryDataset)
|
||||
.setOverwrite(bigqueryOverwrite)
|
||||
.setPollInterval(bigqueryPollInterval)
|
||||
.build();
|
||||
connection.initialize();
|
||||
return connection;
|
||||
}
|
||||
|
||||
/** Creates a credential object for the Bigquery client service using a service account. */
|
||||
private GoogleCredential newCredential() throws Exception {
|
||||
return new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
|
||||
.setJsonFactory(JSON_FACTORY)
|
||||
.setServiceAccountId(bigqueryServiceAccountEmail)
|
||||
.setServiceAccountScopes(BigqueryScopes.all())
|
||||
.setServiceAccountPrivateKeyFromP12File(bigqueryServiceAccountKeyFile.toFile())
|
||||
.build();
|
||||
}
|
||||
}
|
91
java/google/registry/tools/CanonicalizeLabelsCommand.java
Normal file
91
java/google/registry/tools/CanonicalizeLabelsCommand.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;
|
||||
|
||||
import static com.google.domain.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.util.DomainNameUtils;
|
||||
import com.google.domain.registry.util.Idn;
|
||||
import com.google.domain.registry.util.NonFinalForTesting;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/** Command to clean up a set of labels and turn them into punycode. */
|
||||
@Parameters(commandDescription = "Canonicalize domain labels")
|
||||
final class CanonicalizeLabelsCommand implements Command, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Filename of file containing domain labels, one per line",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@NonFinalForTesting
|
||||
private static InputStream stdin = System.in;
|
||||
|
||||
@Override
|
||||
public void run() throws IOException {
|
||||
Set<String> labels = new TreeSet<>();
|
||||
for (String label : mainParameters.isEmpty()
|
||||
? CharStreams.readLines(new InputStreamReader(stdin))
|
||||
: Files.readLines(new File(mainParameters.get(0)), UTF_8)) {
|
||||
label = label.trim();
|
||||
if (label.startsWith("-")) {
|
||||
label = label.substring(1);
|
||||
}
|
||||
if (label.endsWith("-")) {
|
||||
label = label.substring(0, label.length() - 1);
|
||||
}
|
||||
String canonical = canonicalize(label);
|
||||
if (canonical.startsWith(DomainNameUtils.ACE_PREFIX)
|
||||
&& Idn.toUnicode(canonical).equals(canonical)) {
|
||||
System.err.println("Bad IDN: " + label);
|
||||
continue; // Bad IDN code points.
|
||||
}
|
||||
labels.add(canonical);
|
||||
if (!canonical.startsWith("xn--")) {
|
||||
// Using both "" and "-" to canonicalize labels.
|
||||
labels.add(canonicalize(label.replaceAll(" ", "")));
|
||||
labels.add(canonicalize(label.replaceAll(" ", "-")));
|
||||
labels.add(canonicalize(label.replaceAll("_", "")));
|
||||
labels.add(canonicalize(label.replaceAll("_", "-")));
|
||||
}
|
||||
}
|
||||
labels.remove(""); // We used "" for invalid labels.
|
||||
System.out.println(Joiner.on('\n').join(labels));
|
||||
}
|
||||
|
||||
private String canonicalize(String rawLabel) {
|
||||
try {
|
||||
return canonicalizeDomainName(rawLabel.replaceAll(" ", ""));
|
||||
} catch (Exception e) {
|
||||
System.err.printf("Error canonicalizing %s: %s\n", rawLabel, e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
49
java/google/registry/tools/CheckSnapshotCommand.java
Normal file
49
java/google/registry/tools/CheckSnapshotCommand.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.domain.registry.export.DatastoreBackupInfo;
|
||||
import com.google.domain.registry.export.DatastoreBackupService;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/**
|
||||
* Command to check the status of a datastore backup, or "snapshot".
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Check the status of a datastore snapshot")
|
||||
public class CheckSnapshotCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-s", "--snapshot"},
|
||||
description = "Unique prefix of the snapshot to check",
|
||||
required = true)
|
||||
private String snapshotName;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
Iterable<DatastoreBackupInfo> backups =
|
||||
DatastoreBackupService.get().findAllByNamePrefix(snapshotName);
|
||||
if (Iterables.isEmpty(backups)) {
|
||||
System.err.println("No snapshot found with name: " + snapshotName);
|
||||
return;
|
||||
}
|
||||
for (DatastoreBackupInfo backup : backups) {
|
||||
System.out.println(backup.getInformation());
|
||||
}
|
||||
}
|
||||
}
|
35
java/google/registry/tools/Command.java
Normal file
35
java/google/registry/tools/Command.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
// 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;
|
||||
|
||||
/** Interface of all commands. */
|
||||
public interface Command {
|
||||
|
||||
/** Performs the command. */
|
||||
void run() throws Exception;
|
||||
|
||||
/**
|
||||
* Marker interface for commands that use the remote api.
|
||||
* <p>
|
||||
* Just implementing this is sufficient to use the remote api; {@link RegistryTool} will install
|
||||
* it as needed.
|
||||
*/
|
||||
public interface RemoteApiCommand extends Command {}
|
||||
|
||||
/**
|
||||
* Marker interface for commands that are gTech safe.
|
||||
*/
|
||||
public interface GtechCommand extends Command {}
|
||||
}
|
68
java/google/registry/tools/CommandUtilities.java
Normal file
68
java/google/registry/tools/CommandUtilities.java
Normal file
|
@ -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;
|
||||
|
||||
import static com.google.domain.registry.flows.EppXmlTransformer.marshalWithLenientRetry;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.flows.FlowRunner;
|
||||
import com.google.domain.registry.flows.FlowRunner.CommitMode;
|
||||
import com.google.domain.registry.flows.FlowRunner.UserPrivileges;
|
||||
|
||||
/** Container class for static utility methods. */
|
||||
class CommandUtilities {
|
||||
|
||||
static String addHeader(String header, String body) {
|
||||
return String.format("%s:\n%s\n%s", header, Strings.repeat("-", header.length() + 1), body);
|
||||
}
|
||||
|
||||
/** Prompts for yes/no input using promptText, defaulting to no. */
|
||||
static boolean promptForYes(String promptText) {
|
||||
return promptForYesOrNo(promptText, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts for yes/no input using promptText and returns true for yes and false for no, using
|
||||
* defaultResponse as the response for empty input.
|
||||
*/
|
||||
static boolean promptForYesOrNo(String promptText, boolean defaultResponse) {
|
||||
String options = defaultResponse ? "Y/n" : "y/N";
|
||||
while (true) {
|
||||
String line = System.console().readLine(String.format("%s (%s): ", promptText, options));
|
||||
if (line.isEmpty()) {
|
||||
return defaultResponse;
|
||||
} else if ("Y".equalsIgnoreCase(line.substring(0, 1))) {
|
||||
return true;
|
||||
} else if ("N".equalsIgnoreCase(line.substring(0, 1))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Prints the provided text with a trailing newline, if text is not null or empty. */
|
||||
static void printLineIfNotEmpty(String text) {
|
||||
if (!Strings.isNullOrEmpty(text)) {
|
||||
System.out.println(text);
|
||||
}
|
||||
}
|
||||
|
||||
static String runFlow(
|
||||
FlowRunner flowRunner, CommitMode commitMode, UserPrivileges userPrivileges)
|
||||
throws EppException {
|
||||
return new String(marshalWithLenientRetry(flowRunner.run(commitMode, userPrivileges)), UTF_8);
|
||||
}
|
||||
}
|
67
java/google/registry/tools/ConfirmingCommand.java
Normal file
67
java/google/registry/tools/ConfirmingCommand.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.tools.CommandUtilities.printLineIfNotEmpty;
|
||||
import static com.google.domain.registry.tools.CommandUtilities.promptForYes;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
/** A {@link Command} that implements a confirmation step before executing. */
|
||||
public abstract class ConfirmingCommand implements Command {
|
||||
|
||||
@Parameter(
|
||||
names = {"-f", "--force"},
|
||||
description = "Do not prompt before executing")
|
||||
boolean force;
|
||||
|
||||
@Override
|
||||
public final void run() throws Exception {
|
||||
if (checkExecutionState()) {
|
||||
init();
|
||||
printLineIfNotEmpty(prompt());
|
||||
if (force || promptForYes("Perform this command?")) {
|
||||
System.out.println(execute());
|
||||
printLineIfNotEmpty(postExecute());
|
||||
} else {
|
||||
System.out.println("Command aborted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Run any pre-execute command checks and return true if they all pass. */
|
||||
protected boolean checkExecutionState() throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Initializes the command. */
|
||||
protected void init() throws Exception {}
|
||||
|
||||
/** Returns the optional extra confirmation prompt for the command. */
|
||||
protected String prompt() throws Exception {
|
||||
return "";
|
||||
}
|
||||
|
||||
/** Perform the command and return a result description. */
|
||||
protected abstract String execute() throws Exception;
|
||||
|
||||
/**
|
||||
* Perform any post-execution steps (e.g. verifying the result), and return a description String
|
||||
* to be printed if non-empty.
|
||||
*/
|
||||
protected String postExecute() throws Exception {
|
||||
return "";
|
||||
}
|
||||
}
|
48
java/google/registry/tools/ConvertIdnCommand.java
Normal file
48
java/google/registry/tools/ConvertIdnCommand.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.util.DomainNameUtils.ACE_PREFIX;
|
||||
import static com.google.domain.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.util.Idn;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to convert IDN labels to/from punycode. */
|
||||
@Parameters(commandDescription = "Convert IDNs to/from punycode")
|
||||
final class ConvertIdnCommand implements Command, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Labels to convert",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void run() throws IOException {
|
||||
for (String label : mainParameters) {
|
||||
if (label.startsWith(ACE_PREFIX)) {
|
||||
System.out.println(Idn.toUnicode(label.toLowerCase()));
|
||||
} else {
|
||||
System.out.println(canonicalizeDomainName(label));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
102
java/google/registry/tools/CreateAnchorTenantCommand.java
Normal file
102
java/google/registry/tools/CreateAnchorTenantCommand.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.domain.registry.model.registry.Registries.findTldForNameOrThrow;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.soy.CreateAnchorTenantSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.money.Money;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** A command to create a new anchor tenant domain. */
|
||||
@Parameters(separators = " =", commandDescription = "Provision a domain for an anchor tenant.")
|
||||
final class CreateAnchorTenantCommand extends MutatingEppToolCommand implements GtechCommand {
|
||||
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
private static final int DEFAULT_ANCHOR_TENANT_PERIOD_YEARS = 2;
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@Parameter(
|
||||
names = {"-n", "--domain_name"},
|
||||
description = "Domain to create.",
|
||||
required = true)
|
||||
private String domainName;
|
||||
|
||||
@Parameter(
|
||||
names = {"--contact"},
|
||||
description = "Contact ID for the request. This will be used for registrant, admin contact,"
|
||||
+ "and tech contact.",
|
||||
required = true)
|
||||
private String contact;
|
||||
|
||||
@Parameter(
|
||||
names = {"--reason"},
|
||||
description = "Reason for the change.")
|
||||
private String reason;
|
||||
|
||||
@Parameter(
|
||||
names = {"--password"},
|
||||
description = "Password. Optional, randomly generated if not provided.")
|
||||
private String password;
|
||||
|
||||
@Parameter(
|
||||
names = {"--fee"},
|
||||
description = "Include fee extension in EPP (required for premium domains).")
|
||||
private boolean fee;
|
||||
|
||||
@Inject
|
||||
PasswordGenerator passwordGenerator;
|
||||
|
||||
@Override
|
||||
void initMutatingEppToolCommand() {
|
||||
checkArgument(superuser, "This command must be run as a superuser.");
|
||||
String tld = findTldForNameOrThrow(InternetDomainName.from(domainName)).toString();
|
||||
if (isNullOrEmpty(password)) {
|
||||
password = passwordGenerator.createPassword(PASSWORD_LENGTH);
|
||||
}
|
||||
|
||||
Money cost = null;
|
||||
if (fee) {
|
||||
cost = Registry.get(tld).getDomainCreateCost(domainName, DEFAULT_ANCHOR_TENANT_PERIOD_YEARS);
|
||||
}
|
||||
|
||||
setSoyTemplate(CreateAnchorTenantSoyInfo.getInstance(),
|
||||
CreateAnchorTenantSoyInfo.CREATEANCHORTENANT);
|
||||
addSoyRecord(clientIdentifier, new SoyMapData(
|
||||
"domainName", domainName,
|
||||
"contactId", contact,
|
||||
"reason", reason,
|
||||
"password", password,
|
||||
"period", DEFAULT_ANCHOR_TENANT_PERIOD_YEARS,
|
||||
"feeCurrency", cost != null ? cost.getCurrencyUnit().toString() : null,
|
||||
"fee", cost != null ? cost.getAmount().toString() : null));
|
||||
}
|
||||
}
|
215
java/google/registry/tools/CreateAuctionCreditsCommand.java
Normal file
215
java/google/registry/tools/CreateAuctionCreditsCommand.java
Normal file
|
@ -0,0 +1,215 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
|
||||
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit;
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit.CreditType;
|
||||
import com.google.domain.registry.model.billing.RegistrarCreditBalance;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.money.BigMoney;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Command for creating new auction credits based on a CSV file from Pool.
|
||||
* <p>
|
||||
* The CSV file from the auction provider uses double-quotes around every field, so in order to
|
||||
* extract the raw field value we strip off the quotes after splitting each line by commas. We are
|
||||
* using a simple parsing strategy that does not support embedded quotation marks, commas, or
|
||||
* newlines.
|
||||
* <p>
|
||||
* TODO(b/16009815): Switch this file to using a real CSV parser.
|
||||
* <p>
|
||||
* Example file format:
|
||||
* <pre>
|
||||
* "Affiliate","DomainName","Email","BidderId","BidderStatus","UpdatedAt",
|
||||
* "SalePrice","Commissions","CurrencyCode"
|
||||
* "reg1","foo.xn--q9jyb4c","email1@example.com","???_300","INACTIVE","4/3/2014 7:13:09 PM",
|
||||
* "1000.0000","0.0000","JPY"
|
||||
* "reg2","foo.xn--q9jyb4c","email2@example.net","???_64","WIN","4/3/2014 7:13:09 PM",
|
||||
* "1000.0000","40.0000","JPY"
|
||||
* </pre>
|
||||
* We only care about three fields: 1) the "Affiliate" field which corresponds to the registrar
|
||||
* clientId stored in datastore, and which we use to determine which registrar gets the credit,
|
||||
* 2) the "Commissions" field which contains the amount of the auction credit (as determined by
|
||||
* logic on the auction provider's side, see the Finance Requirements Doc for more information), and
|
||||
* 3) the "CurrencyCode" field, which we validate matches the TLD-wide currency for this TLD.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Create new auction credits based on CSV")
|
||||
final class CreateAuctionCreditsCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--input_file",
|
||||
description = "CSV file for the Pool.com commissions report",
|
||||
required = true)
|
||||
private Path inputFile;
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "The TLD corresponding to this commissions report",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = "--effective_time",
|
||||
description = "The time at which these auction credits should become effective",
|
||||
required = true)
|
||||
private DateTime effectiveTime;
|
||||
|
||||
/** Enum containing the headers we expect in the Pool.com CSV file, in order. */
|
||||
private enum CsvHeader {
|
||||
AFFILIATE,
|
||||
DOMAIN_NAME,
|
||||
EMAIL,
|
||||
BIDDER_ID,
|
||||
BIDDER_STATUS,
|
||||
UPDATED_AT,
|
||||
SALE_PRICE,
|
||||
COMMISSIONS,
|
||||
CURRENCY_CODE;
|
||||
|
||||
public static List<String> getHeaders() {
|
||||
return FluentIterable.from(asList(values()))
|
||||
.transform(new Function<CsvHeader, String>() {
|
||||
@Override
|
||||
public String apply(CsvHeader header) {
|
||||
// Returns the name of the header as it appears in the CSV file.
|
||||
return UPPER_UNDERSCORE.to(UPPER_CAMEL, header.name());
|
||||
}})
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern QUOTED_STRING = Pattern.compile("\"(.*)\"");
|
||||
|
||||
/** Helper function to unwrap a quoted string, failing if the string is not quoted. */
|
||||
private static final Function<String, String> UNQUOTER = new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
Matcher matcher = QUOTED_STRING.matcher(input);
|
||||
checkArgument(matcher.matches(), "Input not quoted");
|
||||
return matcher.group(1);
|
||||
}};
|
||||
|
||||
/** Returns the input string of quoted CSV values split into the list of unquoted values. */
|
||||
private static List<String> splitCsvLine(String line) {
|
||||
return FluentIterable.from(Splitter.on(',').split(line)).transform(UNQUOTER).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
assertTldExists(tld);
|
||||
ImmutableMultimap<Registrar, BigMoney> creditMap = parseCreditsFromCsv(inputFile, tld);
|
||||
stageCreditCreations(creditMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the provided CSV file of data from the auction provider and returns a multimap mapping
|
||||
* each registrar to the collection of auction credit amounts from this TLD's auctions that should
|
||||
* be awarded to this registrar, and validating that every credit amount's currency is in the
|
||||
* specified TLD-wide currency.
|
||||
*/
|
||||
private static ImmutableMultimap<Registrar, BigMoney> parseCreditsFromCsv(
|
||||
Path csvFile, String tld) throws IOException {
|
||||
List<String> lines = Files.readAllLines(csvFile, StandardCharsets.UTF_8);
|
||||
checkArgument(CsvHeader.getHeaders().equals(splitCsvLine(lines.get(0))),
|
||||
"Expected CSV header line not present");
|
||||
ImmutableMultimap.Builder<Registrar, BigMoney> builder = new ImmutableMultimap.Builder<>();
|
||||
for (String line : Iterables.skip(lines, 1)) {
|
||||
List<String> fields = splitCsvLine(line);
|
||||
checkArgument(CsvHeader.getHeaders().size() == fields.size(), "Wrong number of fields");
|
||||
try {
|
||||
String registrarId = fields.get(CsvHeader.AFFILIATE.ordinal());
|
||||
Registrar registrar = checkNotNull(
|
||||
Registrar.loadByClientId(registrarId), "Registrar %s not found", registrarId);
|
||||
CurrencyUnit tldCurrency = Registry.get(tld).getCurrency();
|
||||
CurrencyUnit currency = CurrencyUnit.of((fields.get(CsvHeader.CURRENCY_CODE.ordinal())));
|
||||
checkArgument(tldCurrency.equals(currency),
|
||||
"Credit in wrong currency (%s should be %s)", currency, tldCurrency);
|
||||
// We use BigDecimal and BigMoney to preserve fractional currency units when computing the
|
||||
// total amount of each credit (since auction credits are percentages of winning bids).
|
||||
BigDecimal creditAmount = new BigDecimal(fields.get(CsvHeader.COMMISSIONS.ordinal()));
|
||||
BigMoney credit = BigMoney.of(currency, creditAmount);
|
||||
builder.put(registrar, credit);
|
||||
} catch (IllegalArgumentException | IndexOutOfBoundsException e) {
|
||||
throw new IllegalArgumentException("Error in line: " + line, e);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stages the creation of RegistrarCredit and RegistrarCreditBalance instances for each
|
||||
* registrar in the provided multimap of credit amounts by registrar. The balance instance
|
||||
* created is the total of all the credit amounts for a given registrar.
|
||||
*/
|
||||
private void stageCreditCreations(ImmutableMultimap<Registrar, BigMoney> creditMap) {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
CurrencyUnit currency = Registry.get(tld).getCurrency();
|
||||
for (Registrar registrar : creditMap.keySet()) {
|
||||
// Use RoundingMode.UP to be nice and give registrars the extra fractional units.
|
||||
Money totalAmount =
|
||||
BigMoney.total(currency, creditMap.get(registrar)).toMoney(RoundingMode.UP);
|
||||
System.out.printf("Total auction credit balance for %s: %s\n",
|
||||
registrar.getClientIdentifier(), totalAmount);
|
||||
|
||||
// Create the actual credit and initial credit balance.
|
||||
RegistrarCredit credit = new RegistrarCredit.Builder()
|
||||
.setParent(registrar)
|
||||
.setType(CreditType.AUCTION)
|
||||
.setCreationTime(now)
|
||||
.setCurrency(currency)
|
||||
.setTld(tld)
|
||||
.build();
|
||||
RegistrarCreditBalance creditBalance = new RegistrarCreditBalance.Builder()
|
||||
.setParent(credit)
|
||||
.setEffectiveTime(effectiveTime)
|
||||
.setWrittenTime(now)
|
||||
.setAmount(totalAmount)
|
||||
.build();
|
||||
stageEntityChange(null, credit);
|
||||
stageEntityChange(null, creditBalance);
|
||||
}
|
||||
}
|
||||
}
|
137
java/google/registry/tools/CreateContactCommand.java
Normal file
137
java/google/registry/tools/CreateContactCommand.java
Normal file
|
@ -0,0 +1,137 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.params.PhoneNumberParameter;
|
||||
import com.google.domain.registry.tools.soy.CreateContactSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** A command to create a new contact via EPP. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a new contact via EPP.")
|
||||
final class CreateContactCommand extends MutatingEppToolCommand implements GtechCommand {
|
||||
// TODO(b/19016175): Expand to allow full suite of contact flows.
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@Parameter(
|
||||
names = {"--id"},
|
||||
description = "Contact ID.")
|
||||
private String id;
|
||||
|
||||
@Parameter(
|
||||
names = {"--name"},
|
||||
description = "Contact name.")
|
||||
private String name;
|
||||
|
||||
@Parameter(
|
||||
names = {"--org"},
|
||||
description = "Organization")
|
||||
private String org;
|
||||
|
||||
@Parameter(
|
||||
names = {"--street"},
|
||||
description = "Street lines of address. Can take up to 3 lines.",
|
||||
variableArity = true)
|
||||
private List<String> street;
|
||||
|
||||
@Parameter(
|
||||
names = {"--city"},
|
||||
description = "City of address.")
|
||||
private String city;
|
||||
|
||||
@Parameter(
|
||||
names = {"--state"},
|
||||
description = "State of address.")
|
||||
private String state;
|
||||
|
||||
@Parameter(
|
||||
names = {"--zip"},
|
||||
description = "Postal code of address.")
|
||||
private String zip;
|
||||
|
||||
@Parameter(
|
||||
names = {"--cc"},
|
||||
description = "Country code of address.")
|
||||
private String cc;
|
||||
|
||||
@Parameter(
|
||||
names = "--phone",
|
||||
description = "E.164 phone number, e.g. +1.2125650666",
|
||||
converter = PhoneNumberParameter.class,
|
||||
validateWith = PhoneNumberParameter.class)
|
||||
String phone;
|
||||
|
||||
@Parameter(
|
||||
names = "--fax",
|
||||
description = "E.164 fax number, e.g. +1.2125650666",
|
||||
converter = PhoneNumberParameter.class,
|
||||
validateWith = PhoneNumberParameter.class)
|
||||
String fax;
|
||||
|
||||
@Parameter(
|
||||
names = {"--email"},
|
||||
description = "Email address.")
|
||||
private String email;
|
||||
|
||||
@Parameter(
|
||||
names = {"--password"},
|
||||
description = "Password. Optional, randomly generated if not provided.")
|
||||
private String password;
|
||||
|
||||
@Inject
|
||||
PasswordGenerator passwordGenerator;
|
||||
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
|
||||
@Override
|
||||
void initMutatingEppToolCommand() {
|
||||
if (isNullOrEmpty(password)) {
|
||||
password = passwordGenerator.createPassword(PASSWORD_LENGTH);
|
||||
}
|
||||
|
||||
checkArgument(street == null || street.size() <= 3,
|
||||
"Addresses must contain at most 3 street lines.");
|
||||
|
||||
setSoyTemplate(CreateContactSoyInfo.getInstance(),
|
||||
CreateContactSoyInfo.CREATECONTACT);
|
||||
addSoyRecord(clientIdentifier, new SoyMapData(
|
||||
"id", id,
|
||||
"name", name,
|
||||
"org", org,
|
||||
"street", street,
|
||||
"city", city,
|
||||
"state", state,
|
||||
"zip", zip,
|
||||
"cc", cc,
|
||||
"phone", phone,
|
||||
"fax", fax,
|
||||
"email", email,
|
||||
"password", password));
|
||||
}
|
||||
}
|
78
java/google/registry/tools/CreateCreditBalanceCommand.java
Normal file
78
java/google/registry/tools/CreateCreditBalanceCommand.java
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit;
|
||||
import com.google.domain.registry.model.billing.RegistrarCreditBalance;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.util.SystemClock;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command for creating a new balance for a registrar credit. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a new registrar credit balance")
|
||||
final class CreateCreditBalanceCommand extends MutatingCommand implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--registrar",
|
||||
description = "Client ID of the registrar owning the credit to create a new balance for",
|
||||
required = true)
|
||||
private String registrarId;
|
||||
|
||||
@Parameter(
|
||||
names = "--credit_id",
|
||||
description = "ID of credit to create a new balance for",
|
||||
required = true)
|
||||
private long creditId;
|
||||
|
||||
@Parameter(
|
||||
names = "--balance",
|
||||
description = "The new balance amount",
|
||||
required = true)
|
||||
private Money balance;
|
||||
|
||||
@Parameter(
|
||||
names = "--effective_time",
|
||||
description = "Point in time at which the new balance amount becomes effective",
|
||||
required = true)
|
||||
private DateTime effectiveTime;
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
Registrar registrar = checkNotNull(
|
||||
Registrar.loadByClientId(registrarId), "Registrar %s not found", registrarId);
|
||||
RegistrarCredit credit = ofy().load()
|
||||
.type(RegistrarCredit.class)
|
||||
.parent(registrar)
|
||||
.id(creditId)
|
||||
.now();
|
||||
checkNotNull(credit, "Registrar credit for %s with ID %s not found", registrarId, creditId);
|
||||
RegistrarCreditBalance newBalance = new RegistrarCreditBalance.Builder()
|
||||
.setParent(credit)
|
||||
.setEffectiveTime(effectiveTime)
|
||||
.setWrittenTime(new SystemClock().nowUtc())
|
||||
.setAmount(balance)
|
||||
.build();
|
||||
stageEntityChange(null, newBalance);
|
||||
}
|
||||
}
|
96
java/google/registry/tools/CreateCreditCommand.java
Normal file
96
java/google/registry/tools/CreateCreditCommand.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit;
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit.CreditType;
|
||||
import com.google.domain.registry.model.billing.RegistrarCreditBalance;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command for creating a registrar credit object with an initial balance. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a new registrar credit")
|
||||
final class CreateCreditCommand extends MutatingCommand implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--registrar",
|
||||
description = "Client ID of the registrar who will be awarded this credit",
|
||||
required = true)
|
||||
private String registrarId;
|
||||
|
||||
@Parameter(
|
||||
names = "--type",
|
||||
description = "Type of credit (AUCTION or PROMOTION)",
|
||||
required = true)
|
||||
private CreditType type;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--description",
|
||||
description = "Custom description that will appear on invoice line for this credit")
|
||||
private String description;
|
||||
|
||||
@Parameter(
|
||||
names = "--tld",
|
||||
description = "TLD for which this credit applies",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = "--balance",
|
||||
description = "Initial balance of this credit",
|
||||
required = true)
|
||||
private Money balance;
|
||||
|
||||
@Parameter(
|
||||
names = "--effective_time",
|
||||
description = "Point in time at which the initial balance becomes effective",
|
||||
required = true)
|
||||
private DateTime effectiveTime;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
Registrar registrar = checkNotNull(
|
||||
Registrar.loadByClientId(registrarId), "Registrar %s not found", registrarId);
|
||||
RegistrarCredit credit = new RegistrarCredit.Builder()
|
||||
.setParent(registrar)
|
||||
.setType(type)
|
||||
.setCreationTime(now)
|
||||
.setCurrency(balance.getCurrencyUnit())
|
||||
.setDescription(description)
|
||||
.setTld(tld)
|
||||
.build();
|
||||
RegistrarCreditBalance creditBalance = new RegistrarCreditBalance.Builder()
|
||||
.setParent(credit)
|
||||
.setEffectiveTime(effectiveTime)
|
||||
.setWrittenTime(now)
|
||||
.setAmount(balance)
|
||||
.build();
|
||||
stageEntityChange(null, credit);
|
||||
stageEntityChange(null, creditBalance);
|
||||
}
|
||||
}
|
143
java/google/registry/tools/CreateOrUpdatePremiumListCommand.java
Normal file
143
java/google/registry/tools/CreateOrUpdatePremiumListCommand.java
Normal file
|
@ -0,0 +1,143 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.domain.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
||||
import static com.google.domain.registry.tools.server.CreateOrUpdatePremiumListAction.INPUT_PARAM;
|
||||
import static com.google.domain.registry.tools.server.CreateOrUpdatePremiumListAction.NAME_PARAM;
|
||||
import static com.google.domain.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Verify;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.domain.registry.model.registry.label.PremiumList;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Base class for specification of command line parameters common to creating and updating premium
|
||||
* lists.
|
||||
*/
|
||||
abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand
|
||||
implements ServerSideCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
description = "The name of this premium list (defaults to filename if not specified). "
|
||||
+ "This is almost always the name of the TLD this premium list will be used on.")
|
||||
String name;
|
||||
|
||||
@Parameter(
|
||||
names = {"-i", "--input"},
|
||||
description = "Filename of premium list to create or update.",
|
||||
validateWith = PathParameter.InputFile.class,
|
||||
required = true)
|
||||
Path inputFile;
|
||||
|
||||
protected Connection connection;
|
||||
protected int inputLineCount;
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
abstract String getCommandPath();
|
||||
|
||||
ImmutableMap<String, ? extends Object> getParameterMap() {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
name = isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
List<String> lines = Files.readAllLines(inputFile, UTF_8);
|
||||
// Try constructing the premium list locally to check up front for validation errors.
|
||||
new PremiumList.Builder()
|
||||
.setName(name)
|
||||
.setPremiumListMapFromLines(lines)
|
||||
.build();
|
||||
inputLineCount = lines.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() throws Exception {
|
||||
return String.format(
|
||||
"You are about to save the premium list %s with %d items: ", name, inputLineCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute() throws Exception {
|
||||
ImmutableMap.Builder<String, Object> params = new ImmutableMap.Builder<>();
|
||||
params.put(NAME_PARAM, name);
|
||||
String inputFileContents = new String(Files.readAllBytes(inputFile), UTF_8);
|
||||
String requestBody =
|
||||
Joiner.on('&').withKeyValueSeparator("=").join(
|
||||
ImmutableMap.of(INPUT_PARAM, URLEncoder.encode(inputFileContents, UTF_8.toString())));
|
||||
|
||||
ImmutableMap<String, ?> extraParams = getParameterMap();
|
||||
if (extraParams != null) {
|
||||
params.putAll(extraParams);
|
||||
}
|
||||
|
||||
// Call the server and get the response data
|
||||
String response = connection.send(
|
||||
getCommandPath(),
|
||||
params.build(),
|
||||
MediaType.FORM_DATA,
|
||||
requestBody.getBytes());
|
||||
|
||||
return extractServerResponse(response);
|
||||
}
|
||||
|
||||
// TODO(tjb): refactor this behavior into a better general-purpose
|
||||
// response validation that can be re-used across the new client/server commands.
|
||||
String extractServerResponse(String response) {
|
||||
Map<String, Object> responseMap = toMap(JSONValue.parse(stripJsonPrefix(response)));
|
||||
|
||||
// TODO(tjb): consider using jart's FormField Framework.
|
||||
// See: j/c/g/d/r/ui/server/RegistrarFormFields.java
|
||||
String status = (String) responseMap.get("status");
|
||||
Verify.verify(!status.equals("error"), "Server error: %s", responseMap.get("error"));
|
||||
return String.format("Successfully saved premium list %s\n", name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> toMap(Object obj) {
|
||||
Verify.verify(obj instanceof Map<?, ?>, "JSON object is not a Map: %s", obj);
|
||||
return (Map<String, Object>) obj;
|
||||
}
|
||||
|
||||
// TODO(tjb): figure out better place to put this method to make it re-usable
|
||||
static String stripJsonPrefix(String json) {
|
||||
Verify.verify(json.startsWith(JSON_SAFETY_PREFIX));
|
||||
return json.substring(JSON_SAFETY_PREFIX.length());
|
||||
}
|
||||
}
|
408
java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
Normal file
408
java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
Normal file
|
@ -0,0 +1,408 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Predicates.isNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.domain.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
import static com.google.domain.registry.util.RegistrarUtils.normalizeRegistrarName;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.domain.registry.model.billing.RegistrarBillingUtils;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.registrar.Registrar.BillingMethod;
|
||||
import com.google.domain.registry.model.registrar.RegistrarAddress;
|
||||
import com.google.domain.registry.tools.params.OptionalLongParameter;
|
||||
import com.google.domain.registry.tools.params.OptionalPhoneNumberParameter;
|
||||
import com.google.domain.registry.tools.params.OptionalStringParameter;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
import com.google.domain.registry.util.CidrAddressBlock;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Shared base class for commands to create or update a {@link Registrar}. */
|
||||
abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Client identifier of the registrar account",
|
||||
required = true)
|
||||
List<String> mainParameters;
|
||||
|
||||
@Parameter(
|
||||
names = "--registrar_type",
|
||||
description = "Type of the registrar")
|
||||
Registrar.Type registrarType;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--registrar_state",
|
||||
description = "Initial state of the registrar")
|
||||
Registrar.State registrarState;
|
||||
|
||||
@Parameter(
|
||||
names = "--allowed_tlds",
|
||||
description = "Comma-delimited list of TLDs which the registrar is allowed to use")
|
||||
List<String> allowedTlds = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = "--add_allowed_tlds",
|
||||
description = "Comma-delimited list of TLDs to add to TLDs a registrar is allowed to use")
|
||||
List<String> addAllowedTlds = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--password",
|
||||
description = "Password for the registrar account")
|
||||
String password;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--name",
|
||||
description = "Name of the registrar")
|
||||
String registrarName;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--email",
|
||||
description = "Email address of registrar",
|
||||
converter = OptionalStringParameter.class,
|
||||
validateWith = OptionalStringParameter.class)
|
||||
Optional<String> email;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--icann_referral_email",
|
||||
description = "ICANN referral email, as specified in registrar contract")
|
||||
String icannReferralEmail;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--url",
|
||||
description = "URL of registrar's website",
|
||||
converter = OptionalStringParameter.class,
|
||||
validateWith = OptionalStringParameter.class)
|
||||
private Optional<String> url;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--phone",
|
||||
description = "E.164 phone number, e.g. +1.2125650666",
|
||||
converter = OptionalPhoneNumberParameter.class,
|
||||
validateWith = OptionalPhoneNumberParameter.class)
|
||||
Optional<String> phone;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--fax",
|
||||
description = "E.164 fax number, e.g. +1.2125650666",
|
||||
converter = OptionalPhoneNumberParameter.class,
|
||||
validateWith = OptionalPhoneNumberParameter.class)
|
||||
Optional<String> fax;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--cert_file",
|
||||
description = "File containing client certificate (X.509 PEM)",
|
||||
validateWith = PathParameter.InputFile.class)
|
||||
Path clientCertificateFilename;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--cert_hash",
|
||||
description = "Hash of client certificate (SHA256 base64 no padding). Do not use this unless "
|
||||
+ "you want to store ONLY the hash and not the full certificate")
|
||||
private String clientCertificateHash;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--failover_cert_file",
|
||||
description = "File containing failover client certificate (X.509 PEM)",
|
||||
validateWith = PathParameter.InputFile.class)
|
||||
Path failoverClientCertificateFilename;
|
||||
|
||||
@Parameter(
|
||||
names = "--ip_whitelist",
|
||||
description = "Comma-delimited list of IP ranges")
|
||||
List<String> ipWhitelist = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--iana_id",
|
||||
description = "Registrar IANA ID",
|
||||
converter = OptionalLongParameter.class,
|
||||
validateWith = OptionalLongParameter.class)
|
||||
Optional<Long> ianaId;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--billing_id",
|
||||
description = "Registrar Billing ID (i.e. Oracle #)",
|
||||
converter = OptionalLongParameter.class,
|
||||
validateWith = OptionalLongParameter.class)
|
||||
private Optional<Long> billingId;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--billing_method",
|
||||
description = "Method by which registry bills this registrar customer")
|
||||
private BillingMethod billingMethod;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--street",
|
||||
variableArity = true,
|
||||
description = "Street lines of address. Can take up to 3 lines.")
|
||||
List<String> street;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--city",
|
||||
description = "City of address")
|
||||
String city;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--state",
|
||||
description = "State/Province of address. The value \"null\" clears this field.")
|
||||
String state;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--zip",
|
||||
description = "Postal code of address. The value \"null\" clears this field.")
|
||||
String zip;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--cc",
|
||||
description = "Country code of address")
|
||||
String countryCode;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--block_premium",
|
||||
description = "Whether premium name registration should be blocked on this registrar",
|
||||
arity = 1)
|
||||
private Boolean blockPremiumNames;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--sync_groups",
|
||||
description = "Whether this registrar's groups should be updated at the next scheduled sync",
|
||||
arity = 1)
|
||||
private Boolean contactsRequireSyncing;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--drive_id",
|
||||
description = "Id of this registrar's folder in Drive",
|
||||
converter = OptionalStringParameter.class,
|
||||
validateWith = OptionalStringParameter.class)
|
||||
Optional<String> driveFolderId;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--passcode",
|
||||
description = "Telephone support passcode")
|
||||
String phonePasscode;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--whois",
|
||||
description = "Hostname of registrar WHOIS server. (Default: whois.nic.google)")
|
||||
String whoisServer;
|
||||
|
||||
/** Returns the existing registrar (for update) or null (for creates). */
|
||||
@Nullable
|
||||
abstract Registrar getOldRegistrar(String clientIdentifier);
|
||||
|
||||
protected void initRegistrarCommand() throws Exception {}
|
||||
|
||||
@Override
|
||||
protected final void init() throws Exception {
|
||||
initRegistrarCommand();
|
||||
DateTime now = DateTime.now(UTC);
|
||||
for (String clientIdentifier : mainParameters) {
|
||||
Registrar oldRegistrar = getOldRegistrar(clientIdentifier);
|
||||
Registrar.Builder builder = (oldRegistrar == null)
|
||||
? new Registrar.Builder().setClientIdentifier(clientIdentifier)
|
||||
: oldRegistrar.asBuilder();
|
||||
|
||||
if (!isNullOrEmpty(password)) {
|
||||
builder.setPassword(password);
|
||||
}
|
||||
if (!isNullOrEmpty(registrarName)) {
|
||||
builder.setRegistrarName(registrarName);
|
||||
}
|
||||
if (email != null) {
|
||||
builder.setEmailAddress(email.orNull());
|
||||
}
|
||||
if (url != null) {
|
||||
builder.setUrl(url.orNull());
|
||||
}
|
||||
if (phone != null) {
|
||||
builder.setPhoneNumber(phone.orNull());
|
||||
}
|
||||
if (fax != null) {
|
||||
builder.setFaxNumber(fax.orNull());
|
||||
}
|
||||
if (registrarType != null) {
|
||||
builder.setType(registrarType);
|
||||
}
|
||||
if (registrarState != null) {
|
||||
builder.setState(registrarState);
|
||||
}
|
||||
if (driveFolderId != null) {
|
||||
builder.setDriveFolderId(driveFolderId.orNull());
|
||||
}
|
||||
if (!allowedTlds.isEmpty()) {
|
||||
checkArgument(addAllowedTlds.isEmpty(),
|
||||
"Can't specify both --allowedTlds and --addAllowedTlds");
|
||||
ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>();
|
||||
for (String allowedTld : allowedTlds) {
|
||||
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
|
||||
}
|
||||
builder.setAllowedTlds(allowedTldsBuilder.build());
|
||||
}
|
||||
if (!addAllowedTlds.isEmpty()) {
|
||||
ImmutableSet.Builder<String> allowedTldsBuilder = new ImmutableSet.Builder<>();
|
||||
if (oldRegistrar != null) {
|
||||
allowedTldsBuilder.addAll(oldRegistrar.getAllowedTlds());
|
||||
}
|
||||
for (String allowedTld : addAllowedTlds) {
|
||||
allowedTldsBuilder.add(canonicalizeDomainName(allowedTld));
|
||||
}
|
||||
builder.setAllowedTlds(allowedTldsBuilder.build());
|
||||
}
|
||||
if (!ipWhitelist.isEmpty()) {
|
||||
ImmutableList.Builder<CidrAddressBlock> ipWhitelistBuilder = new ImmutableList.Builder<>();
|
||||
if (!(ipWhitelist.size() == 1 && ipWhitelist.get(0).contains("null"))) {
|
||||
for (String ipRange : ipWhitelist) {
|
||||
ipWhitelistBuilder.add(CidrAddressBlock.create(ipRange));
|
||||
}
|
||||
}
|
||||
builder.setIpAddressWhitelist(ipWhitelistBuilder.build());
|
||||
}
|
||||
if (clientCertificateFilename != null) {
|
||||
String asciiCert = new String(Files.readAllBytes(clientCertificateFilename), US_ASCII);
|
||||
builder.setClientCertificate(asciiCert, now);
|
||||
}
|
||||
if (failoverClientCertificateFilename != null) {
|
||||
String asciiCert =
|
||||
new String(Files.readAllBytes(failoverClientCertificateFilename), US_ASCII);
|
||||
builder.setFailoverClientCertificate(asciiCert, now);
|
||||
}
|
||||
if (!isNullOrEmpty(clientCertificateHash)) {
|
||||
checkArgument(clientCertificateFilename == null,
|
||||
"Can't specify both --cert_hash and --cert_file");
|
||||
if ("null".equals(clientCertificateHash)) {
|
||||
clientCertificateHash = null;
|
||||
}
|
||||
builder.setClientCertificateHash(clientCertificateHash);
|
||||
}
|
||||
if (ianaId != null) {
|
||||
builder.setIanaIdentifier(ianaId.orNull());
|
||||
}
|
||||
if (billingId != null) {
|
||||
builder.setBillingIdentifier(billingId.orNull());
|
||||
}
|
||||
if (billingMethod != null) {
|
||||
if (oldRegistrar != null && !billingMethod.equals(oldRegistrar.getBillingMethod())) {
|
||||
Map<CurrencyUnit, Money> balances = RegistrarBillingUtils.loadBalance(oldRegistrar);
|
||||
for (Money balance : balances.values()) {
|
||||
checkState(balance.isZero(),
|
||||
"Refusing to change billing method on Registrar '%s' from %s to %s"
|
||||
+ " because current balance is non-zero: %s",
|
||||
clientIdentifier, oldRegistrar.getBillingMethod(), billingMethod, balances);
|
||||
}
|
||||
}
|
||||
builder.setBillingMethod(billingMethod);
|
||||
}
|
||||
List<Object> streetAddressFields = Arrays.asList(street, city, state, zip, countryCode);
|
||||
checkArgument(Iterables.any(streetAddressFields, isNull())
|
||||
== Iterables.all(streetAddressFields, isNull()),
|
||||
"Must specify all fields of address");
|
||||
if (street != null) {
|
||||
// We always set the localized address for now. That should be safe to do since it supports
|
||||
// unrestricted UTF-8.
|
||||
builder.setLocalizedAddress(new RegistrarAddress.Builder()
|
||||
.setStreet(ImmutableList.copyOf(street))
|
||||
.setCity(city)
|
||||
.setState("null".equals(state) ? null : state)
|
||||
.setZip("null".equals(zip) ? null : zip)
|
||||
.setCountryCode(countryCode)
|
||||
.build());
|
||||
}
|
||||
if (blockPremiumNames != null) {
|
||||
builder.setBlockPremiumNames(blockPremiumNames);
|
||||
}
|
||||
if (contactsRequireSyncing != null) {
|
||||
builder.setContactsRequireSyncing(contactsRequireSyncing);
|
||||
}
|
||||
// When creating a new REAL registrar or changing the type to REAL, a passcode is required.
|
||||
// Leave existing REAL registrars alone.
|
||||
if (Registrar.Type.REAL.equals(registrarType)
|
||||
&& (oldRegistrar == null || oldRegistrar.getPhonePasscode() == null)) {
|
||||
checkArgument(phonePasscode != null, "--passcode is required for REAL registrars.");
|
||||
}
|
||||
if (phonePasscode != null) {
|
||||
builder.setPhonePasscode(phonePasscode);
|
||||
}
|
||||
if (icannReferralEmail != null) {
|
||||
builder.setIcannReferralEmail(icannReferralEmail);
|
||||
}
|
||||
if (whoisServer != null) {
|
||||
builder.setWhoisServer(whoisServer);
|
||||
}
|
||||
|
||||
// If the registrarName is being set, verify that it is either null or it normalizes uniquely.
|
||||
String oldRegistrarName = (oldRegistrar == null) ? null : oldRegistrar.getRegistrarName();
|
||||
if (registrarName != null && !registrarName.equals(oldRegistrarName)) {
|
||||
String normalizedName = normalizeRegistrarName(registrarName);
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
if (registrar.getRegistrarName() != null) {
|
||||
checkArgument(
|
||||
!normalizedName.equals(normalizeRegistrarName(registrar.getRegistrarName())),
|
||||
"The registrar name %s normalizes identically to existing registrar name %s",
|
||||
registrarName,
|
||||
registrar.getRegistrarName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stageEntityChange(oldRegistrar, builder.build());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Base class for specification of command line parameters common to creating and updating reserved
|
||||
* lists.
|
||||
*/
|
||||
public abstract class CreateOrUpdateReservedListCommand extends MutatingCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
description = "The name of this reserved list (defaults to filename if not specified).")
|
||||
String name;
|
||||
|
||||
@Parameter(
|
||||
names = {"-i", "--input"},
|
||||
description = "Filename of new reserved list.",
|
||||
validateWith = PathParameter.InputFile.class,
|
||||
required = true)
|
||||
Path input;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--should_publish",
|
||||
description =
|
||||
"Whether the list is published to the concatenated list on Drive (defaults to true).",
|
||||
arity = 1)
|
||||
Boolean shouldPublish;
|
||||
}
|
453
java/google/registry/tools/CreateOrUpdateTldCommand.java
Normal file
453
java/google/registry/tools/CreateOrUpdateTldCommand.java
Normal file
|
@ -0,0 +1,453 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static com.google.domain.registry.model.RoidSuffixes.isRoidSuffixUsed;
|
||||
import static com.google.domain.registry.util.CollectionUtils.findDuplicates;
|
||||
import static com.google.domain.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static com.google.domain.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.domain.registry.model.registry.Registries;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.model.registry.Registry.TldType;
|
||||
import com.google.domain.registry.model.registry.label.PremiumList;
|
||||
import com.google.domain.registry.model.registry.label.ReservedList;
|
||||
import com.google.domain.registry.tools.params.OptionalStringParameter;
|
||||
import com.google.domain.registry.tools.params.TransitionListParameter.BillingCostTransitions;
|
||||
import com.google.domain.registry.tools.params.TransitionListParameter.TldStateTransitions;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.googlecode.objectify.Key;
|
||||
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Shared base class for commands to create or update a TLD. */
|
||||
abstract class CreateOrUpdateTldCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Names of the TLDs",
|
||||
required = true)
|
||||
List<String> mainParameters;
|
||||
|
||||
@Parameter(
|
||||
names = "--escrow",
|
||||
description = "Whether to enable nightly RDE escrow deposits",
|
||||
arity = 1)
|
||||
private Boolean escrow;
|
||||
|
||||
@Parameter(
|
||||
names = "--dns",
|
||||
description = "Set to false to pause writing to the DNS queue",
|
||||
arity = 1)
|
||||
private Boolean dns;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--add_grace_period",
|
||||
description = "Length of the add grace period")
|
||||
Duration addGracePeriod;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--redemption_grace_period",
|
||||
description = "Length of the redemption grace period")
|
||||
Duration redemptionGracePeriod;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--pending_delete_length",
|
||||
description = "Length of the pending delete period")
|
||||
Duration pendingDeleteLength;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--automatic_transfer_length",
|
||||
description = "Length of the automatic transfer period")
|
||||
private Duration automaticTransferLength;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--restore_billing_cost",
|
||||
description = "One-time billing cost for restoring a domain")
|
||||
private Money restoreBillingCost;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--roid_suffix",
|
||||
description = "The suffix to be used for ROIDs, e.g. COM for .com domains (which then "
|
||||
+ "creates roids looking like 123ABC-COM)")
|
||||
String roidSuffix;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--server_status_change_cost",
|
||||
description = "One-time billing cost for a server status change")
|
||||
private Money serverStatusChangeCost;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--tld_type",
|
||||
description = "Tld type (REAL or TEST)")
|
||||
private TldType tldType;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--premium_price_ack_required",
|
||||
description = "Whether operations on premium domains require explicit ack of prices",
|
||||
arity = 1)
|
||||
private Boolean premiumPriceAckRequired;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--create_billing_cost",
|
||||
description = "Per-year billing cost for creating a domain")
|
||||
Money createBillingCost;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--drive_folder_id",
|
||||
description = "Id of the folder in drive used to publish information for this TLD",
|
||||
converter = OptionalStringParameter.class,
|
||||
validateWith = OptionalStringParameter.class)
|
||||
Optional<String> driveFolderId;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--lordn_username",
|
||||
description = "Username for LORDN uploads",
|
||||
converter = OptionalStringParameter.class,
|
||||
validateWith = OptionalStringParameter.class)
|
||||
Optional<String> lordnUsername;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--premium_list",
|
||||
description = "The name of the premium list to apply to the TLD",
|
||||
converter = OptionalStringParameter.class,
|
||||
validateWith = OptionalStringParameter.class)
|
||||
Optional<String> premiumListName;
|
||||
|
||||
@Parameter(
|
||||
names = "--tld_state_transitions",
|
||||
converter = TldStateTransitions.class,
|
||||
validateWith = TldStateTransitions.class,
|
||||
description = "Comma-delimited list of TLD state transitions, of the form "
|
||||
+ "<time>=<tld-state>[,<time>=<tld-state>]*")
|
||||
ImmutableSortedMap<DateTime, TldState> tldStateTransitions = ImmutableSortedMap.of();
|
||||
|
||||
@Parameter(
|
||||
names = "--renew_billing_cost_transitions",
|
||||
converter = BillingCostTransitions.class,
|
||||
validateWith = BillingCostTransitions.class,
|
||||
description = "Comma-delimited list of renew billing cost transitions, of the form "
|
||||
+ "<time>=<money-amount>[,<time>=<money-amount>]* where each amount "
|
||||
+ "represents the per-year billing cost for renewing a domain")
|
||||
ImmutableSortedMap<DateTime, Money> renewBillingCostTransitions =
|
||||
ImmutableSortedMap.of();
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--reserved_lists",
|
||||
description = "A comma-separated list of reserved list names to be applied to the TLD")
|
||||
List<String> reservedListNames;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--allowed_registrants",
|
||||
description = "A comma-separated list of allowed registrants for the TLD")
|
||||
List<String> allowedRegistrants;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--allowed_nameservers",
|
||||
description = "A comma-separated list of allowed nameservers for the TLD")
|
||||
List<String> allowedNameservers;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--override_reserved_list_rules"},
|
||||
description = "Override restrictions on reserved list naming")
|
||||
boolean overrideReservedListRules;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--claims_period_end",
|
||||
description = "The end of the claims period")
|
||||
DateTime claimsPeriodEnd;
|
||||
|
||||
@Nullable
|
||||
Set<String> reservedListNamesToAdd;
|
||||
|
||||
@Nullable
|
||||
Set<String> reservedListNamesToRemove;
|
||||
|
||||
@Nullable
|
||||
Set<String> allowedRegistrantsToAdd;
|
||||
|
||||
@Nullable
|
||||
Set<String> allowedRegistrantsToRemove;
|
||||
|
||||
@Nullable
|
||||
Set<String> allowedNameserversToAdd;
|
||||
|
||||
@Nullable
|
||||
Set<String> allowedNameserversToRemove;
|
||||
|
||||
/** Returns the existing registry (for update) or null (for creates). */
|
||||
@Nullable
|
||||
abstract Registry getOldRegistry(String tld);
|
||||
|
||||
/** Subclasses can override this to set their own properties. */
|
||||
void setCommandSpecificProperties(@SuppressWarnings("unused") Registry.Builder builder) {}
|
||||
|
||||
/** Subclasses can override this to assert that the command can be run in this environment. */
|
||||
void assertAllowedEnvironment() {}
|
||||
|
||||
protected abstract void initTldCommand() throws Exception;
|
||||
|
||||
@Override
|
||||
protected final void init() throws Exception {
|
||||
assertAllowedEnvironment();
|
||||
initTldCommand();
|
||||
String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters));
|
||||
checkArgument(duplicates.isEmpty(), "Duplicate arguments found: \"%s\"", duplicates);
|
||||
Set<String> tlds = ImmutableSet.copyOf(mainParameters);
|
||||
checkArgument(roidSuffix == null || tlds.size() == 1,
|
||||
"Can't update roid suffixes on multiple TLDs simultaneously");
|
||||
for (String tld : tlds) {
|
||||
checkArgument(tld.equals(canonicalizeDomainName(tld)));
|
||||
checkArgument(
|
||||
!CharMatcher.javaDigit().matches(tld.charAt(0)),
|
||||
"TLDs cannot begin with a number.");
|
||||
Registry oldRegistry = getOldRegistry(tld);
|
||||
if (roidSuffix != null) {
|
||||
checkArgument(
|
||||
!isRoidSuffixUsed(roidSuffix)
|
||||
|| (oldRegistry != null && roidSuffix.equals(oldRegistry.getRoidSuffix())),
|
||||
"The roid suffix %s is already in use",
|
||||
roidSuffix);
|
||||
}
|
||||
Registry.Builder builder = oldRegistry == null
|
||||
? new Registry.Builder().setTldStr(tld) : oldRegistry.asBuilder();
|
||||
|
||||
if (escrow != null) {
|
||||
builder.setEscrowEnabled(escrow);
|
||||
}
|
||||
|
||||
if (dns != null) {
|
||||
builder.setDnsPaused(!dns);
|
||||
}
|
||||
|
||||
if (!tldStateTransitions.isEmpty()) {
|
||||
builder.setTldStateTransitions(tldStateTransitions);
|
||||
}
|
||||
|
||||
if (!renewBillingCostTransitions.isEmpty()) {
|
||||
// TODO(b/20764952): need invoicing support for multiple renew billing costs.
|
||||
if (renewBillingCostTransitions.size() > 1) {
|
||||
System.err.println(
|
||||
"----------------------\n"
|
||||
+ "WARNING: Do not set multiple renew cost transitions until b/20764952 is fixed.\n"
|
||||
+ "----------------------\n");
|
||||
}
|
||||
builder.setRenewBillingCostTransitions(renewBillingCostTransitions);
|
||||
}
|
||||
|
||||
if (addGracePeriod != null) {
|
||||
builder.setAddGracePeriodLength(addGracePeriod);
|
||||
}
|
||||
|
||||
if (redemptionGracePeriod != null) {
|
||||
builder.setRedemptionGracePeriodLength(redemptionGracePeriod);
|
||||
}
|
||||
|
||||
if (pendingDeleteLength != null) {
|
||||
builder.setPendingDeleteLength(pendingDeleteLength);
|
||||
}
|
||||
|
||||
if (automaticTransferLength != null) {
|
||||
builder.setAutomaticTransferLength(automaticTransferLength);
|
||||
}
|
||||
|
||||
if (driveFolderId != null) {
|
||||
builder.setDriveFolderId(driveFolderId.orNull());
|
||||
}
|
||||
|
||||
if (createBillingCost != null) {
|
||||
builder.setCreateBillingCost(createBillingCost);
|
||||
}
|
||||
|
||||
if (restoreBillingCost != null) {
|
||||
builder.setRestoreBillingCost(restoreBillingCost);
|
||||
}
|
||||
|
||||
if (roidSuffix != null) {
|
||||
builder.setRoidSuffix(roidSuffix);
|
||||
}
|
||||
|
||||
if (serverStatusChangeCost != null) {
|
||||
builder.setServerStatusChangeBillingCost(serverStatusChangeCost);
|
||||
}
|
||||
|
||||
if (tldType != null) {
|
||||
builder.setTldType(tldType);
|
||||
}
|
||||
|
||||
if (premiumPriceAckRequired != null) {
|
||||
builder.setPremiumPriceAckRequired(premiumPriceAckRequired);
|
||||
}
|
||||
|
||||
if (lordnUsername != null) {
|
||||
builder.setLordnUsername(lordnUsername.orNull());
|
||||
}
|
||||
|
||||
if (claimsPeriodEnd != null) {
|
||||
builder.setClaimsPeriodEnd(claimsPeriodEnd);
|
||||
}
|
||||
|
||||
if (premiumListName != null) {
|
||||
if (premiumListName.isPresent()) {
|
||||
Optional<PremiumList> premiumList = PremiumList.get(premiumListName.get());
|
||||
checkArgument(premiumList.isPresent(),
|
||||
String.format("The premium list '%s' doesn't exist", premiumListName.get()));
|
||||
builder.setPremiumList(premiumList.get());
|
||||
} else {
|
||||
builder.setPremiumList(null);
|
||||
}
|
||||
}
|
||||
|
||||
ImmutableSet<String> newReservedListNames =
|
||||
formUpdatedList(
|
||||
"reserved lists",
|
||||
oldRegistry == null ? ImmutableSet.<String>of() : FluentIterable
|
||||
.from(oldRegistry.getReservedLists())
|
||||
.transform(
|
||||
new Function<Key<ReservedList>, String>() {
|
||||
@Override
|
||||
public String apply(Key<ReservedList> key) {
|
||||
return key.getName();
|
||||
}})
|
||||
.toSet(),
|
||||
reservedListNames,
|
||||
reservedListNamesToAdd,
|
||||
reservedListNamesToRemove);
|
||||
checkReservedListValidityForTld(tld, newReservedListNames);
|
||||
builder.setReservedListsByName(newReservedListNames);
|
||||
|
||||
builder.setAllowedRegistrantContactIds(
|
||||
formUpdatedList(
|
||||
"allowed registrants",
|
||||
oldRegistry == null
|
||||
? ImmutableSet.<String>of()
|
||||
: oldRegistry.getAllowedRegistrantContactIds(),
|
||||
allowedRegistrants,
|
||||
allowedRegistrantsToAdd,
|
||||
allowedRegistrantsToRemove));
|
||||
|
||||
builder.setAllowedFullyQualifiedHostNames(
|
||||
formUpdatedList(
|
||||
"allowed nameservers",
|
||||
oldRegistry == null
|
||||
? ImmutableSet.<String>of()
|
||||
: oldRegistry.getAllowedFullyQualifiedHostNames(),
|
||||
allowedNameservers,
|
||||
allowedNameserversToAdd,
|
||||
allowedNameserversToRemove));
|
||||
|
||||
// Update the Registry object.
|
||||
setCommandSpecificProperties(builder);
|
||||
stageEntityChange(oldRegistry, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private ImmutableSet<String> formUpdatedList(
|
||||
String description,
|
||||
ImmutableSet<String> originals,
|
||||
List<String> toReplace,
|
||||
Set<String> toAdd,
|
||||
Set<String> toRemove) {
|
||||
if (toReplace != null) {
|
||||
return ImmutableSet.copyOf(toReplace);
|
||||
}
|
||||
toAdd = nullToEmpty(toAdd);
|
||||
toRemove = nullToEmpty(toRemove);
|
||||
checkIsEmpty(
|
||||
intersection(toAdd, toRemove),
|
||||
String.format(
|
||||
"Adding and removing the same %s simultaneously doesn't make sense", description));
|
||||
checkIsEmpty(
|
||||
intersection(originals, toAdd),
|
||||
String.format("Cannot add %s that were previously present", description));
|
||||
checkIsEmpty(
|
||||
difference(toRemove, originals),
|
||||
String.format("Cannot remove %s that were not previously present", description));
|
||||
return ImmutableSet.copyOf(difference(union(originals, toAdd), toRemove));
|
||||
}
|
||||
|
||||
private void checkIsEmpty(Set<String> set, String errorString) {
|
||||
checkArgument(set.isEmpty(), String.format("%s: %s", errorString, set));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute() throws Exception {
|
||||
try {
|
||||
return super.execute();
|
||||
} finally {
|
||||
// Manually reset the cache here so that subsequent commands (e.g. in SetupOteCommand) see
|
||||
// the latest version of the data.
|
||||
// TODO(b/24903801): change all those places to use uncached code paths to get Registries.
|
||||
Registries.resetCache();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkReservedListValidityForTld(String tld, Set<String> reservedListNames) {
|
||||
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
|
||||
for (String reservedListName : reservedListNames) {
|
||||
if (!reservedListName.startsWith("common_") && !reservedListName.startsWith(tld + "_")) {
|
||||
builder.add(reservedListName);
|
||||
}
|
||||
}
|
||||
ImmutableList<String> invalidNames = builder.build();
|
||||
if (!invalidNames.isEmpty()) {
|
||||
String errMsg = String.format("The reserved list(s) %s cannot be applied to the tld %s",
|
||||
Joiner.on(", ").join(invalidNames),
|
||||
tld);
|
||||
if (overrideReservedListRules) {
|
||||
System.err.println("Error overriden: " + errMsg);
|
||||
} else {
|
||||
throw new IllegalArgumentException(errMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
java/google/registry/tools/CreatePremiumListCommand.java
Normal file
51
java/google/registry/tools/CreatePremiumListCommand.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.model.registry.label.PremiumList;
|
||||
import com.google.domain.registry.tools.server.CreatePremiumListAction;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command to create a {@link PremiumList} on Datastore. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a PremiumList in Datastore.")
|
||||
public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-o", "--override"},
|
||||
description = "Override restrictions on premium list naming")
|
||||
boolean override;
|
||||
|
||||
/** Returns the path to the servlet task. */
|
||||
@Override
|
||||
public String getCommandPath() {
|
||||
return CreatePremiumListAction.PATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableMap<String, ? extends Object> getParameterMap() {
|
||||
if (override) {
|
||||
return ImmutableMap.of("override", override);
|
||||
} else {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
131
java/google/registry/tools/CreateRegistrarCommand.java
Normal file
131
java/google/registry/tools/CreateRegistrarCommand.java
Normal file
|
@ -0,0 +1,131 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static com.google.domain.registry.model.registrar.Registrar.State.ACTIVE;
|
||||
import static com.google.domain.registry.tools.RegistryToolEnvironment.PRODUCTION;
|
||||
import static com.google.domain.registry.tools.RegistryToolEnvironment.SANDBOX;
|
||||
import static com.google.domain.registry.tools.RegistryToolEnvironment.UNITTEST;
|
||||
import static com.google.domain.registry.util.RegistrarUtils.normalizeClientId;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command to create a Registrar. */
|
||||
@Parameters(separators = " =", commandDescription = "Create new registrar account(s)")
|
||||
final class CreateRegistrarCommand extends CreateOrUpdateRegistrarCommand
|
||||
implements GtechCommand, ServerSideCommand {
|
||||
|
||||
private static final ImmutableSet<RegistryToolEnvironment> ENVIRONMENTS_ALLOWING_GROUP_CREATION =
|
||||
ImmutableSet.of(PRODUCTION, SANDBOX, UNITTEST);
|
||||
|
||||
// Allows test cases to be cleaner.
|
||||
@VisibleForTesting
|
||||
static boolean requireAddress = true;
|
||||
|
||||
@Parameter(
|
||||
names = "--create_groups",
|
||||
description = "Whether the Google Groups for this registrar should be created",
|
||||
arity = 1)
|
||||
boolean createGoogleGroups = true;
|
||||
|
||||
private Connection connection;
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initRegistrarCommand() throws Exception {
|
||||
checkArgument(mainParameters.size() == 1, "Must specify exactly one client identifier.");
|
||||
checkNotNull(emptyToNull(password), "--password is a required field");
|
||||
checkNotNull(registrarName, "--name is a required field");
|
||||
checkNotNull(icannReferralEmail, "--icann_referral_email is a required field");
|
||||
if (requireAddress) {
|
||||
checkNotNull(street, "Address fields are required when creating a registrar");
|
||||
}
|
||||
// Default new registrars to active.
|
||||
registrarState = Optional.fromNullable(registrarState).or(ACTIVE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
Registrar getOldRegistrar(final String clientIdentifier) {
|
||||
checkArgument(clientIdentifier.length() >= 3,
|
||||
String.format("Client identifier (%s) is too short", clientIdentifier));
|
||||
checkArgument(clientIdentifier.length() <= 16,
|
||||
String.format("Client identifier (%s) is too long", clientIdentifier));
|
||||
if (Registrar.Type.REAL.equals(registrarType)) {
|
||||
checkArgument(clientIdentifier.equals(normalizeClientId(clientIdentifier)),
|
||||
String.format(
|
||||
"Client identifier (%s) can only contain lowercase letters, numbers, and hyphens",
|
||||
clientIdentifier));
|
||||
}
|
||||
checkState(Registrar.loadByClientId(clientIdentifier) == null,
|
||||
"Registrar %s already exists", clientIdentifier);
|
||||
List<Registrar> collisions =
|
||||
newArrayList(filter(Registrar.loadAll(), new Predicate<Registrar>() {
|
||||
@Override
|
||||
public boolean apply(Registrar registrar) {
|
||||
return normalizeClientId(registrar.getClientIdentifier()).equals(clientIdentifier);
|
||||
}}));
|
||||
if (!collisions.isEmpty()) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The registrar client identifier %s normalizes identically to existing registrar %s",
|
||||
clientIdentifier,
|
||||
collisions.get(0).getClientIdentifier()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String postExecute() throws Exception {
|
||||
if (!createGoogleGroups) {
|
||||
return "";
|
||||
}
|
||||
// Allow prod and sandbox because they actually have Groups, and UNITTEST for testing.
|
||||
if (!ENVIRONMENTS_ALLOWING_GROUP_CREATION.contains(RegistryToolEnvironment.get())) {
|
||||
return "\nSkipping registrar groups creation because only production and sandbox support it.";
|
||||
}
|
||||
try {
|
||||
// We know it is safe to use the only main parameter here because initRegistrarCommand has
|
||||
// already verified that there is only one, and getOldRegistrar has already verified that a
|
||||
// registrar with this clientIdentifier doesn't already exist.
|
||||
CreateRegistrarGroupsCommand.executeOnServer(connection, getOnlyElement(mainParameters));
|
||||
} catch (Exception e) {
|
||||
return "\nRegistrar created, but groups creation failed with error:\n" + e;
|
||||
}
|
||||
return "\nRegistrar groups created successfully.";
|
||||
}
|
||||
}
|
99
java/google/registry/tools/CreateRegistrarGroupsCommand.java
Normal file
99
java/google/registry/tools/CreateRegistrarGroupsCommand.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.domain.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.server.CreateGroupsAction;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to create groups in Google Groups for all contact types for a registrar.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Create groups for a registrar.")
|
||||
public class CreateRegistrarGroupsCommand extends ConfirmingCommand
|
||||
implements ServerSideCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Client identifier(s) of the registrar(s) to create groups for",
|
||||
required = true)
|
||||
private List<String> clientIds;
|
||||
|
||||
private List<Registrar> registrars = new ArrayList<>();
|
||||
|
||||
private Connection connection;
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() throws IOException {
|
||||
for (String clientId : clientIds) {
|
||||
Registrar registrar = Registrar.loadByClientId(clientId);
|
||||
checkArgumentNotNull(registrar, "Could not load registrar with id " + clientId);
|
||||
registrars.add(registrar);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return String.format(
|
||||
"Create registrar contact groups for registrar(s) %s?",
|
||||
Joiner.on(", ").join(transform(registrars, new Function<Registrar, String>() {
|
||||
@Override
|
||||
public String apply(Registrar registrar) {
|
||||
return registrar.getRegistrarName();
|
||||
}})));
|
||||
}
|
||||
|
||||
/** Calls the server endpoint to create groups for the specified registrar client id. */
|
||||
static void executeOnServer(Connection connection, String clientIdentifier) throws IOException {
|
||||
connection.send(
|
||||
CreateGroupsAction.PATH,
|
||||
ImmutableMap.of(CreateGroupsAction.CLIENT_ID_PARAM, clientIdentifier),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws IOException {
|
||||
for (Registrar registrar : registrars) {
|
||||
connection.send(
|
||||
CreateGroupsAction.PATH,
|
||||
ImmutableMap.of(CreateGroupsAction.CLIENT_ID_PARAM, registrar.getClientIdentifier()),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
}
|
||||
// Note: If any of the calls fail, then a 5XX response code is returned inside of send(), which
|
||||
// throws an exception yielding a stack trace. If we get to this next line then we succeeded.
|
||||
return "Success!";
|
||||
}
|
||||
}
|
||||
|
80
java/google/registry/tools/CreateReservedListCommand.java
Normal file
80
java/google/registry/tools/CreateReservedListCommand.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.domain.registry.model.registry.label.ReservedList;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to create a {@link ReservedList} on Datastore. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a ReservedList in datastore.")
|
||||
final class CreateReservedListCommand extends CreateOrUpdateReservedListCommand {
|
||||
|
||||
@VisibleForTesting
|
||||
static final String INVALID_FORMAT_ERROR_MESSAGE =
|
||||
"The name must be in the format {tld|common}_list-name "
|
||||
+ "and contain only letters, numbers, and hyphens, plus a single underscore delimiter";
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--override"},
|
||||
description = "Override restrictions on reserved list naming")
|
||||
boolean override;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(input) : name;
|
||||
checkArgument(
|
||||
!ReservedList.get(name).isPresent(),
|
||||
"A reserved list already exists by this name");
|
||||
if (!override) {
|
||||
validateListName(name);
|
||||
}
|
||||
DateTime now = DateTime.now(UTC);
|
||||
ReservedList reservedList =
|
||||
new ReservedList.Builder()
|
||||
.setName(name)
|
||||
.setReservedListMapFromLines(Files.readAllLines(input, UTF_8))
|
||||
.setShouldPublish(shouldPublish == null || shouldPublish)
|
||||
.setCreationTime(now)
|
||||
.setLastUpdateTime(now)
|
||||
.build();
|
||||
stageEntityChange(null, reservedList);
|
||||
}
|
||||
|
||||
private static void validateListName(String name) {
|
||||
List<String> nameParts = Splitter.on('_').splitToList(name);
|
||||
checkArgument(nameParts.size() == 2, INVALID_FORMAT_ERROR_MESSAGE);
|
||||
String tld = nameParts.get(0);
|
||||
if (!tld.equals("common")) {
|
||||
assertTldExists(tld);
|
||||
}
|
||||
checkArgument(nameParts.get(1).matches("[-a-zA-Z0-9]+"), INVALID_FORMAT_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
34
java/google/registry/tools/CreateSandboxTldCommand.java
Normal file
34
java/google/registry/tools/CreateSandboxTldCommand.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command to create a TLD in sandbox, separated out for Gtech use. */
|
||||
@Parameters(separators = " =", commandDescription = "Create new sandbox TLD(s)")
|
||||
final class CreateSandboxTldCommand extends CreateTldCommand implements GtechCommand {
|
||||
|
||||
@Override
|
||||
void assertAllowedEnvironment() {
|
||||
checkArgument(
|
||||
RegistryEnvironment.get() == RegistryEnvironment.SANDBOX,
|
||||
"This command can only be run in the sandbox environment");
|
||||
}
|
||||
}
|
83
java/google/registry/tools/CreateTldCommand.java
Normal file
83
java/google/registry/tools/CreateTldCommand.java
Normal file
|
@ -0,0 +1,83 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.domain.registry.model.registry.Registries.getTlds;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.money.Money;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command to create a TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "Create new TLD(s)")
|
||||
class CreateTldCommand extends CreateOrUpdateTldCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--initial_tld_state",
|
||||
description = "Initial state of the TLD (cannot be combined with a transitions list)")
|
||||
TldState initialTldState;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--initial_renew_billing_cost",
|
||||
description = "Initial per-year billing cost for renewing a domain "
|
||||
+ "(cannot be combined with a transitions list)")
|
||||
private Money initialRenewBillingCost;
|
||||
|
||||
@Override
|
||||
protected void initTldCommand() throws Exception {
|
||||
checkArgument(initialTldState == null || tldStateTransitions.isEmpty(),
|
||||
"Don't pass both --initial_tld_state and --tld_state_transitions");
|
||||
if (initialTldState != null) {
|
||||
tldStateTransitions = ImmutableSortedMap.of(START_OF_TIME, initialTldState);
|
||||
}
|
||||
checkArgument(initialRenewBillingCost == null || renewBillingCostTransitions.isEmpty(),
|
||||
"Don't pass both --initial_renew_billing_cost and --renew_billing_cost_transitions");
|
||||
if (initialRenewBillingCost != null) {
|
||||
renewBillingCostTransitions = ImmutableSortedMap.of(START_OF_TIME, initialRenewBillingCost);
|
||||
}
|
||||
checkArgument(mainParameters.size() == 1, "Can't create more than one TLD at a time");
|
||||
checkArgument(
|
||||
!Strings.isNullOrEmpty(roidSuffix),
|
||||
"The roid suffix is required when creating a TLD");
|
||||
}
|
||||
|
||||
@Override
|
||||
void setCommandSpecificProperties(Registry.Builder builder) {
|
||||
// Pick up the currency from the create cost. Since all costs must be in one currency, and that
|
||||
// condition is enforced by the builder, it doesn't matter which cost we choose it from.
|
||||
builder.setCurrency(createBillingCost != null
|
||||
? createBillingCost.getCurrencyUnit()
|
||||
: Registry.DEFAULT_CURRENCY);
|
||||
}
|
||||
|
||||
@Override
|
||||
Registry getOldRegistry(String tld) {
|
||||
checkState(!getTlds().contains(tld), "TLD already exists");
|
||||
return null;
|
||||
}
|
||||
}
|
60
java/google/registry/tools/DeleteCreditCommand.java
Normal file
60
java/google/registry/tools/DeleteCreditCommand.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit;
|
||||
import com.google.domain.registry.model.billing.RegistrarCreditBalance;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command for deleting a registrar credit object and all of its child balances. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete a registrar credit")
|
||||
final class DeleteCreditCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--registrar",
|
||||
description = "Client ID of the registrar owning the credit to delete",
|
||||
required = true)
|
||||
private String registrarId;
|
||||
|
||||
@Parameter(
|
||||
names = "--credit_id",
|
||||
description = "ID of credit to delete",
|
||||
required = true)
|
||||
private long creditId;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
Registrar registrar =
|
||||
checkNotNull(Registrar.loadByClientId(registrarId), "Registrar %s not found", registrarId);
|
||||
RegistrarCredit credit = ofy().load()
|
||||
.type(RegistrarCredit.class)
|
||||
.parent(registrar)
|
||||
.id(creditId)
|
||||
.now();
|
||||
checkNotNull(credit, "Registrar credit for %s with ID %s not found", registrarId, creditId);
|
||||
stageEntityChange(credit, null);
|
||||
|
||||
for (RegistrarCreditBalance balance :
|
||||
ofy().load().type(RegistrarCreditBalance.class).ancestor(credit)) {
|
||||
stageEntityChange(balance, null);
|
||||
}
|
||||
}
|
||||
}
|
60
java/google/registry/tools/DeleteDomainCommand.java
Normal file
60
java/google/registry/tools/DeleteDomainCommand.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.soy.DeleteDomainSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** A command to delete a domain via EPP. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete domain")
|
||||
final class DeleteDomainCommand extends MutatingEppToolCommand implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@Parameter(
|
||||
names = {"-n", "--domain_name"},
|
||||
description = "Domain to delete.",
|
||||
required = true)
|
||||
private String domainName;
|
||||
|
||||
@Parameter(
|
||||
names = {"--reason"},
|
||||
description = "Reason for the change.",
|
||||
required = true)
|
||||
private String reason;
|
||||
|
||||
@Parameter(
|
||||
names = {"--registrar_request"},
|
||||
description = "Whether the change was requested by a registrar.",
|
||||
arity = 1)
|
||||
private boolean requestedByRegistrar = false;
|
||||
|
||||
@Override
|
||||
void initMutatingEppToolCommand() {
|
||||
setSoyTemplate(DeleteDomainSoyInfo.getInstance(), DeleteDomainSoyInfo.DELETEDOMAIN);
|
||||
addSoyRecord(clientIdentifier, new SoyMapData(
|
||||
"domainName", domainName,
|
||||
"reason", reason,
|
||||
"requestedByRegistrar", requestedByRegistrar));
|
||||
}
|
||||
}
|
69
java/google/registry/tools/DeleteEntityCommand.java
Normal file
69
java/google/registry/tools/DeleteEntityCommand.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;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.domain.registry.tools.server.DeleteEntityAction;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to delete an entity (or 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><b>WARNING:</b> This command 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.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Delete entities from Datastore by raw key.")
|
||||
public class DeleteEntityCommand extends ConfirmingCommand implements ServerSideCommand {
|
||||
|
||||
@Parameter(description = "One or more raw keys of entities to delete.", required = true)
|
||||
private List<String> rawKeyStrings;
|
||||
|
||||
private Connection connection;
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
if (rawKeyStrings.size() == 1) {
|
||||
return "You are about to delete the entity: \n" + rawKeyStrings.get(0);
|
||||
} else {
|
||||
return "You are about to delete the entities: \n" + rawKeyStrings;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
String rawKeysJoined = Joiner.on(",").join(rawKeyStrings);
|
||||
return connection.send(
|
||||
DeleteEntityAction.PATH,
|
||||
ImmutableMap.of(DeleteEntityAction.PARAM_RAW_KEYS, rawKeysJoined),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
}
|
||||
}
|
114
java/google/registry/tools/DeleteEppResourceCommand.java
Normal file
114
java/google/registry/tools/DeleteEppResourceCommand.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.model.index.ForeignKeyIndex;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.tools.params.EppResourceTypeParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Command to soft-delete a list of EppResources of a given type specified by ROIDs. */
|
||||
@Parameters(separators = " =", commandDescription = "Soft-delete EPP resources.")
|
||||
final class DeleteEppResourceCommand extends MutatingCommand {
|
||||
|
||||
private static final String DEFAULT_DELETION_REASON = "Deleted using registry_tool.";
|
||||
|
||||
@Parameter(
|
||||
description = "List of EppResource ROIDs to soft-delete.",
|
||||
required = true)
|
||||
private List<String> roids;
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--type"},
|
||||
description = "EPP resource type.",
|
||||
required = true)
|
||||
private EppResourceTypeParameter resourceType;
|
||||
|
||||
@Parameter(
|
||||
names = {"-r", "--reason"},
|
||||
description = "Deletion reason message.")
|
||||
private String reason;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
ImmutableList.Builder<Key<EppResource>> builder = new ImmutableList.Builder<>();
|
||||
for (String roid : roids) {
|
||||
builder.add(Key.create(resourceType.getType(), roid));
|
||||
}
|
||||
ImmutableList<Key<EppResource>> keys = builder.build();
|
||||
Map<Key<EppResource>, EppResource> resources = ofy().load().keys(keys);
|
||||
for (Key<EppResource> key : keys) {
|
||||
if (resources.containsKey(key)) {
|
||||
EppResource resource = resources.get(key);
|
||||
if (isBeforeOrAt(resource.getDeletionTime(), now)) {
|
||||
System.out.printf("Resource already deleted: %s\n", key);
|
||||
} else {
|
||||
stageEntityChange(resource, resource.asBuilder().setDeletionTime(now).build());
|
||||
HistoryEntry deletionRecord = new HistoryEntry.Builder()
|
||||
.setModificationTime(now)
|
||||
.setBySuperuser(true)
|
||||
.setClientId(resource.getCurrentSponsorClientId())
|
||||
.setParent(key)
|
||||
.setReason(MoreObjects.firstNonNull(reason, DEFAULT_DELETION_REASON))
|
||||
.setType(HistoryEntry.Type.SYNTHETIC)
|
||||
.build();
|
||||
stageEntityChange(null, deletionRecord);
|
||||
handleForeignKeyIndex(key, resource, now);
|
||||
flushTransaction();
|
||||
}
|
||||
} else {
|
||||
System.out.printf("Resource does not exist: %s\n", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleForeignKeyIndex(Key<EppResource> key, EppResource resource, DateTime now) {
|
||||
ForeignKeyIndex<?> fki = ofy().load().key(ForeignKeyIndex.createKey(resource)).now();
|
||||
if (fki == null) {
|
||||
System.out.printf("Creating non-existent ForeignKeyIndex for: %s\n", key);
|
||||
stageEntityChange(null, ForeignKeyIndex.create(resource, now));
|
||||
} else {
|
||||
if (fki.getReference().key().equals(key)) {
|
||||
if (isBeforeOrAt(fki.getDeletionTime(), now)) {
|
||||
System.out.printf("ForeignKeyIndex already deleted for: %s\n", key);
|
||||
} else {
|
||||
// Theoretically this is the only code path that should ever be taken, as there should
|
||||
// always be exactly one non-soft-deleted FKI for every non-soft-deleted EppResource. The
|
||||
// other paths exist to correctly handle potential situations caused by bad data.
|
||||
stageEntityChange(fki, ForeignKeyIndex.create(resource, now));
|
||||
}
|
||||
} else {
|
||||
System.out.printf("Found ForeignKeyIndex pointing to different resource for: %s\n", key);
|
||||
System.out.printf("It was: %s\n", fki);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
73
java/google/registry/tools/DeletePremiumListCommand.java
Normal file
73
java/google/registry/tools/DeletePremiumListCommand.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.registry.label.PremiumList;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* Command to delete a {@link PremiumList} in Datastore. This command will fail if the premium
|
||||
* list is currently in use on a tld.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Delete a PremiumList from Datastore.")
|
||||
final class DeletePremiumListCommand extends ConfirmingCommand implements RemoteApiCommand {
|
||||
|
||||
@Nullable
|
||||
PremiumList premiumList;
|
||||
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
description = "The name of the premium list to delete.",
|
||||
required = true)
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
checkArgument(
|
||||
PremiumList.exists(name),
|
||||
"Cannot delete the premium list %s because it doesn't exist.",
|
||||
name);
|
||||
premiumList = PremiumList.get(name).get();
|
||||
ImmutableSet<String> tldsUsedOn = premiumList.getReferencingTlds();
|
||||
checkArgument(
|
||||
tldsUsedOn.isEmpty(),
|
||||
"Cannot delete premium list because it is used on these tld(s): %s",
|
||||
Joiner.on(", ").join(tldsUsedOn));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return "You are about to delete the premium list: \n" + premiumList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
premiumList.delete();
|
||||
return String.format(
|
||||
"Deleted premium list %s with %d entries.\n",
|
||||
premiumList.getName(),
|
||||
premiumList.getPremiumListEntries().size());
|
||||
}
|
||||
}
|
53
java/google/registry/tools/DeleteReservedListCommand.java
Normal file
53
java/google/registry/tools/DeleteReservedListCommand.java
Normal file
|
@ -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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.registry.label.ReservedList;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/**
|
||||
* Command to delete a {@link ReservedList} in Datastore. This command will fail if the reserved
|
||||
* list is currently in use on a tld.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Deletes a ReservedList in Datastore.")
|
||||
final class DeleteReservedListCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
description = "The name of the reserved list to delete.",
|
||||
required = true)
|
||||
private String name;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
checkArgument(
|
||||
ReservedList.get(name).isPresent(),
|
||||
"Cannot delete the reserved list %s because it doesn't exist.",
|
||||
name);
|
||||
ReservedList existing = ReservedList.get(name).get();
|
||||
ImmutableSet<String> tldsUsedOn = existing.getReferencingTlds();
|
||||
checkArgument(
|
||||
tldsUsedOn.isEmpty(),
|
||||
"Cannot delete reserved list because it is used on these tld(s): %s",
|
||||
Joiner.on(", ").join(tldsUsedOn));
|
||||
stageEntityChange(existing, null);
|
||||
}
|
||||
}
|
69
java/google/registry/tools/DomainApplicationInfoCommand.java
Normal file
69
java/google/registry/tools/DomainApplicationInfoCommand.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;
|
||||
|
||||
import static com.google.domain.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.domain.registry.model.domain.launch.LaunchPhase;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.soy.DomainApplicationInfoSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** A command to execute a domain application info EPP command. */
|
||||
@Parameters(separators = " =", commandDescription = "Get domain application EPP info")
|
||||
final class DomainApplicationInfoCommand extends EppToolCommand implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@Parameter(
|
||||
names = {"--id"},
|
||||
description = "ID of the application.",
|
||||
required = true)
|
||||
private String id;
|
||||
|
||||
@Parameter(
|
||||
names = {"-n", "--domain_name"},
|
||||
description = "Domain name to query.",
|
||||
required = true)
|
||||
private String domainName;
|
||||
|
||||
@Parameter(
|
||||
names = {"--phase"},
|
||||
description = "Phase of the application to query.",
|
||||
required = true)
|
||||
private String phase;
|
||||
|
||||
@Override
|
||||
void initEppToolCommand() {
|
||||
LaunchPhase launchPhase =
|
||||
checkArgumentNotNull(LaunchPhase.fromValue(phase.toLowerCase()), "Illegal launch phase.");
|
||||
|
||||
setSoyTemplate(
|
||||
DomainApplicationInfoSoyInfo.getInstance(),
|
||||
DomainApplicationInfoSoyInfo.DOMAINAPPLICATIONINFO);
|
||||
addSoyRecord(clientIdentifier, new SoyMapData(
|
||||
"domainName", domainName,
|
||||
"id", id,
|
||||
"phase", launchPhase.getPhase(),
|
||||
"subphase", launchPhase.getSubphase()));
|
||||
}
|
||||
}
|
52
java/google/registry/tools/DomainCheckClaimsCommand.java
Normal file
52
java/google/registry/tools/DomainCheckClaimsCommand.java
Normal file
|
@ -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;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.soy.DomainCheckClaimsSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/** A command to execute a domain check claims epp command. */
|
||||
@Parameters(separators = " =", commandDescription = "Check claims on domain(s)")
|
||||
final class DomainCheckClaimsCommand extends EppToolCommand implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@Parameter(
|
||||
description = "Domain(s) to check.",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
void initEppToolCommand() {
|
||||
Multimap<String, String> domainNameMap = validateAndGroupDomainNamesByTld(mainParameters);
|
||||
for (Collection<String> values : domainNameMap.asMap().values()) {
|
||||
setSoyTemplate(
|
||||
DomainCheckClaimsSoyInfo.getInstance(), DomainCheckClaimsSoyInfo.DOMAINCHECKCLAIMS);
|
||||
addSoyRecord(clientIdentifier, new SoyMapData("domainNames", values));
|
||||
}
|
||||
}
|
||||
}
|
51
java/google/registry/tools/DomainCheckCommand.java
Normal file
51
java/google/registry/tools/DomainCheckCommand.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.soy.DomainCheckSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/** A command to execute a domain check epp command. */
|
||||
@Parameters(separators = " =", commandDescription = "Check domain availability")
|
||||
final class DomainCheckCommand extends EppToolCommand implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@Parameter(
|
||||
description = "List of domains to check.",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
void initEppToolCommand() {
|
||||
Multimap<String, String> domainNameMap = validateAndGroupDomainNamesByTld(mainParameters);
|
||||
for (Collection<String> values : domainNameMap.asMap().values()) {
|
||||
setSoyTemplate(DomainCheckSoyInfo.getInstance(), DomainCheckSoyInfo.DOMAINCHECK);
|
||||
addSoyRecord(clientIdentifier, new SoyMapData("domainNames", values));
|
||||
}
|
||||
}
|
||||
}
|
51
java/google/registry/tools/DomainCheckFeeCommand.java
Normal file
51
java/google/registry/tools/DomainCheckFeeCommand.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.soy.DomainCheckFeeSoyInfo;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/** A command to execute a domain check fees epp command. */
|
||||
@Parameters(separators = " =", commandDescription = "Check domain fees (for a 1-year create)")
|
||||
final class DomainCheckFeeCommand extends EppToolCommand implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@Parameter(
|
||||
description = "Domain(s) to check.",
|
||||
required = true)
|
||||
List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
void initEppToolCommand() {
|
||||
Multimap<String, String> domainNameMap = validateAndGroupDomainNamesByTld(mainParameters);
|
||||
for (Collection<String> values : domainNameMap.asMap().values()) {
|
||||
setSoyTemplate(DomainCheckFeeSoyInfo.getInstance(), DomainCheckFeeSoyInfo.DOMAINCHECKFEE);
|
||||
addSoyRecord(clientIdentifier, new SoyMapData("domainNames", values));
|
||||
}
|
||||
}
|
||||
}
|
59
java/google/registry/tools/EncryptEscrowDepositCommand.java
Normal file
59
java/google/registry/tools/EncryptEscrowDepositCommand.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;
|
||||
|
||||
import static com.google.domain.registry.util.DomainNameUtils.canonicalizeDomainName;
|
||||
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to encrypt an escrow deposit. */
|
||||
@Parameters(separators = " =", commandDescription = "Encrypt an escrow deposit")
|
||||
class EncryptEscrowDepositCommand implements Command {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "Top level domain.",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = {"-i", "--input"},
|
||||
description = "Input XML file that was outputted by GenerateEscrowDepositCommand.",
|
||||
validateWith = PathParameter.InputFile.class,
|
||||
required = true)
|
||||
private Path input;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--outdir"},
|
||||
description = "Specify output directory. Default is current directory.",
|
||||
validateWith = PathParameter.OutputDirectory.class)
|
||||
private Path outdir = Paths.get(".");
|
||||
|
||||
@Inject
|
||||
EscrowDepositEncryptor encryptor;
|
||||
|
||||
@Override
|
||||
public final void run() throws Exception {
|
||||
encryptor.encrypt(canonicalizeDomainName(tld), input, outdir);
|
||||
}
|
||||
}
|
166
java/google/registry/tools/EppToolCommand.java
Normal file
166
java/google/registry/tools/EppToolCommand.java
Normal file
|
@ -0,0 +1,166 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Predicates.notNull;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.google.common.collect.Maps.filterValues;
|
||||
import static com.google.common.io.Resources.getResource;
|
||||
import static com.google.domain.registry.flows.EppServletUtils.APPLICATION_EPP_XML_UTF8;
|
||||
import static com.google.domain.registry.model.registry.Registries.findTldForNameOrThrow;
|
||||
import static com.google.domain.registry.tools.CommandUtilities.addHeader;
|
||||
import static com.google.domain.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static com.google.domain.registry.xml.XmlTransformer.prettyPrint;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.template.soy.SoyFileSet;
|
||||
import com.google.template.soy.data.SoyRecord;
|
||||
import com.google.template.soy.parseinfo.SoyFileInfo;
|
||||
import com.google.template.soy.parseinfo.SoyTemplateInfo;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** A command to execute an epp command. */
|
||||
abstract class EppToolCommand extends ConfirmingCommand implements ServerSideCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-u", "--superuser"},
|
||||
description = "Run in superuser mode")
|
||||
boolean superuser = false;
|
||||
|
||||
private SoyFileInfo soyFileInfo;
|
||||
private SoyTemplateInfo soyRenderer;
|
||||
|
||||
private List<XmlEppParameters> commands = new ArrayList<>();
|
||||
|
||||
private Connection connection;
|
||||
|
||||
static class XmlEppParameters {
|
||||
final String clientId;
|
||||
final String xml;
|
||||
|
||||
XmlEppParameters(String clientId, String xml) {
|
||||
this.clientId = clientId;
|
||||
this.xml = xml;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return prettyPrint(xml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for grouping sets of domain names into respective TLDs. Useful for batched
|
||||
* EPP calls when invoking commands (i.e. domain check) with sets of domains across multiple TLDs.
|
||||
*/
|
||||
protected static Multimap<String, String> validateAndGroupDomainNamesByTld(List<String> names) {
|
||||
ImmutableMultimap.Builder<String, String> builder = ImmutableMultimap.builder();
|
||||
for (String name : names) {
|
||||
InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(name));
|
||||
builder.put(tld.toString(), name);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected void setSoyTemplate(SoyFileInfo soyFileInfo, SoyTemplateInfo soyRenderer) {
|
||||
this.soyFileInfo = soyFileInfo;
|
||||
this.soyRenderer = soyRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
protected void addXmlCommand(String clientId, String xml) {
|
||||
checkArgumentNotNull(Registrar.loadByClientId(clientId),
|
||||
"Registrar with client ID %s not found", clientId);
|
||||
commands.add(new XmlEppParameters(clientId, xml));
|
||||
}
|
||||
|
||||
protected void addSoyRecord(String clientId, SoyRecord record) {
|
||||
checkNotNull(soyFileInfo, "SoyFileInfo is missing, cannot add record.");
|
||||
checkNotNull(soyRenderer, "SoyRenderer is missing, cannot add record.");
|
||||
addXmlCommand(clientId, SoyFileSet.builder()
|
||||
.add(getResource(soyFileInfo.getClass(), soyFileInfo.getFileName()))
|
||||
.build()
|
||||
.compileToTofu()
|
||||
.newRenderer(soyRenderer)
|
||||
.setData(record)
|
||||
.render());
|
||||
}
|
||||
|
||||
/** Subclasses can override to implement a dry run flag. False by default. */
|
||||
protected boolean isDryRun() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkExecutionState() throws Exception {
|
||||
checkArgument(!(force && isDryRun()), "--force and --dry_run are incompatible");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prompt() throws IOException {
|
||||
String prompt = addHeader("Command(s)", Joiner.on("\n").join(commands)
|
||||
+ (force ? "" : addHeader("Dry Run", Joiner.on("\n").join(processCommands(true)))));
|
||||
force = force || isDryRun();
|
||||
return prompt.toString();
|
||||
}
|
||||
|
||||
private List<String> processCommands(boolean dryRun) throws IOException {
|
||||
ImmutableList.Builder<String> responses = new ImmutableList.Builder<>();
|
||||
for (XmlEppParameters command : commands) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("dryRun", dryRun);
|
||||
params.put("clientIdentifier", command.clientId);
|
||||
params.put("superuser", superuser);
|
||||
responses.add(nullToEmpty(connection.send(
|
||||
"/_dr/epptool",
|
||||
filterValues(params, notNull()),
|
||||
APPLICATION_EPP_XML_UTF8,
|
||||
command.xml.getBytes(UTF_8))));
|
||||
}
|
||||
return responses.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute() throws Exception {
|
||||
return isDryRun() ? "" : addHeader("Response", Joiner.on("\n").join(processCommands(false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void init() throws Exception {
|
||||
initEppToolCommand();
|
||||
}
|
||||
|
||||
abstract void initEppToolCommand() throws Exception;
|
||||
}
|
96
java/google/registry/tools/EscrowDepositEncryptor.java
Normal file
96
java/google/registry/tools/EscrowDepositEncryptor.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.rde.RdeMode.FULL;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.domain.registry.keyring.api.KeyModule.Key;
|
||||
import com.google.domain.registry.model.rde.RdeNamingUtils;
|
||||
import com.google.domain.registry.rde.RdeUtil;
|
||||
import com.google.domain.registry.rde.RydePgpCompressionOutputStream;
|
||||
import com.google.domain.registry.rde.RydePgpCompressionOutputStreamFactory;
|
||||
import com.google.domain.registry.rde.RydePgpEncryptionOutputStream;
|
||||
import com.google.domain.registry.rde.RydePgpEncryptionOutputStreamFactory;
|
||||
import com.google.domain.registry.rde.RydePgpFileOutputStream;
|
||||
import com.google.domain.registry.rde.RydePgpFileOutputStreamFactory;
|
||||
import com.google.domain.registry.rde.RydePgpSigningOutputStream;
|
||||
import com.google.domain.registry.rde.RydePgpSigningOutputStreamFactory;
|
||||
import com.google.domain.registry.rde.RydeTarOutputStream;
|
||||
import com.google.domain.registry.rde.RydeTarOutputStreamFactory;
|
||||
import com.google.domain.registry.xml.XmlException;
|
||||
|
||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Utility for encrypting an RDE RyDE deposit on the Java 7 NIO file system. */
|
||||
final class EscrowDepositEncryptor {
|
||||
|
||||
private static final int PEEK_BUFFER_SIZE = 64 * 1024;
|
||||
|
||||
@Inject RydePgpCompressionOutputStreamFactory pgpCompressionFactory;
|
||||
@Inject RydePgpEncryptionOutputStreamFactory pgpEncryptionFactory;
|
||||
@Inject RydePgpFileOutputStreamFactory pgpFileFactory;
|
||||
@Inject RydePgpSigningOutputStreamFactory pgpSigningFactory;
|
||||
@Inject RydeTarOutputStreamFactory tarFactory;
|
||||
@Inject @Key("rdeSigningKey") PGPKeyPair rdeSigningKey;
|
||||
@Inject @Key("rdeReceiverKey") PGPPublicKey rdeReceiverKey;
|
||||
@Inject EscrowDepositEncryptor() {}
|
||||
|
||||
/** Creates a {@code .ryde} and {@code .sig} file, provided an XML deposit file. */
|
||||
void encrypt(String tld, Path xmlFile, Path outdir)
|
||||
throws IOException, PGPException, XmlException {
|
||||
try (InputStream xmlFileInput = Files.newInputStream(xmlFile);
|
||||
BufferedInputStream xmlInput = new BufferedInputStream(xmlFileInput, PEEK_BUFFER_SIZE)) {
|
||||
DateTime watermark = RdeUtil.peekWatermark(xmlInput);
|
||||
String name = RdeNamingUtils.makeRydeFilename(tld, watermark, FULL, 1, 0);
|
||||
Path rydePath = outdir.resolve(name + ".ryde");
|
||||
Path sigPath = outdir.resolve(name + ".sig");
|
||||
Path pubPath = outdir.resolve(tld + ".pub");
|
||||
PGPKeyPair signingKey = rdeSigningKey;
|
||||
try (OutputStream rydeOutput = Files.newOutputStream(rydePath);
|
||||
RydePgpSigningOutputStream signLayer =
|
||||
pgpSigningFactory.create(rydeOutput, signingKey)) {
|
||||
try (RydePgpEncryptionOutputStream encryptLayer =
|
||||
pgpEncryptionFactory.create(signLayer, rdeReceiverKey);
|
||||
RydePgpCompressionOutputStream compressLayer =
|
||||
pgpCompressionFactory.create(encryptLayer);
|
||||
RydePgpFileOutputStream fileLayer =
|
||||
pgpFileFactory.create(compressLayer, watermark, name + ".tar");
|
||||
RydeTarOutputStream tarLayer =
|
||||
tarFactory.create(fileLayer, Files.size(xmlFile), watermark, name + ".xml")) {
|
||||
ByteStreams.copy(xmlInput, tarLayer);
|
||||
}
|
||||
Files.write(sigPath, signLayer.getSignature());
|
||||
try (OutputStream pubOutput = Files.newOutputStream(pubPath);
|
||||
ArmoredOutputStream ascOutput = new ArmoredOutputStream(pubOutput)) {
|
||||
signingKey.getPublicKey().encode(ascOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
java/google/registry/tools/ExecuteEppCommand.java
Normal file
59
java/google/registry/tools/ExecuteEppCommand.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;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.domain.registry.util.NonFinalForTesting;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** A command to execute an arbitrary epp command from file or stdin. */
|
||||
@Parameters(separators = " =", commandDescription = "Execute an epp command")
|
||||
final class ExecuteEppCommand extends MutatingEppToolCommand {
|
||||
@Parameter(description = "Epp command filename")
|
||||
private List<String> mainParameters = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--client"},
|
||||
description = "Client identifier of the registrar to execute the command as",
|
||||
required = true)
|
||||
String clientIdentifier;
|
||||
|
||||
@NonFinalForTesting
|
||||
private static InputStream stdin = System.in;
|
||||
|
||||
@Override
|
||||
void initMutatingEppToolCommand() throws IOException {
|
||||
if (mainParameters.isEmpty()) {
|
||||
addXmlCommand(
|
||||
clientIdentifier, CharStreams.toString(new InputStreamReader(stdin, UTF_8)));
|
||||
} else {
|
||||
for (String command : mainParameters) {
|
||||
addXmlCommand(clientIdentifier, Files.toString(new File(command), UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.domain.registry.flows.EppXmlTransformer.unmarshal;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static com.google.domain.registry.util.DomainNameUtils.ACE_PREFIX;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.flows.EppException;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.smd.EncodedSignedMark;
|
||||
import com.google.domain.registry.model.smd.SignedMark;
|
||||
import com.google.domain.registry.model.smd.SignedMarkRevocationList;
|
||||
import com.google.domain.registry.model.tmch.ClaimsListShard;
|
||||
import com.google.domain.registry.tmch.TmchXmlSignature;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
import com.google.domain.registry.util.Idn;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.cmd.LoadType;
|
||||
import com.googlecode.objectify.cmd.Query;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.CheckReturnValue;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to generate a report of all domain applications. */
|
||||
@Parameters(separators = " =", commandDescription = "Generate report of all domain applications.")
|
||||
final class GenerateApplicationsReportCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "TLD which contains the applications.")
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = "--ids",
|
||||
description = "Comma-delimited list of application IDs to include. "
|
||||
+ "If not provided, all active application will be included.")
|
||||
private List<Long> ids = emptyList();
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file.",
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
if (tld != null) {
|
||||
assertTldExists(tld);
|
||||
}
|
||||
DateTime now = clock.nowUtc();
|
||||
List<String> result = new ArrayList<>();
|
||||
if (!ids.isEmpty()) {
|
||||
for (Long applicationId : ids) {
|
||||
DomainApplication domainApplication = ofy().load()
|
||||
.type(DomainApplication.class)
|
||||
.id(applicationId)
|
||||
.now();
|
||||
if (domainApplication == null) {
|
||||
System.err.printf("Application ID %d not found\n", applicationId);
|
||||
continue;
|
||||
}
|
||||
result.addAll(validate(domainApplication, now).asSet());
|
||||
}
|
||||
} else {
|
||||
LoadType<DomainApplication> loader = ofy().load().type(DomainApplication.class);
|
||||
Query<DomainApplication> domainApplications =
|
||||
(tld == null) ? loader : loader.filter("tld", tld);
|
||||
for (DomainApplication domainApplication : domainApplications) {
|
||||
result.addAll(validate(domainApplication, now).asSet());
|
||||
}
|
||||
}
|
||||
Files.write(output, result, UTF_8);
|
||||
}
|
||||
|
||||
/** Processes a domain application and adds report lines to {@code result}. */
|
||||
Optional<String> validate(DomainApplication domainApplication, DateTime now) {
|
||||
// Validate the label.
|
||||
List<String> nameParts =
|
||||
InternetDomainName.from(domainApplication.getFullyQualifiedDomainName()).parts();
|
||||
String label = nameParts.get(0);
|
||||
|
||||
// Ignore deleted applications.
|
||||
if (isBeforeOrAt(domainApplication.getDeletionTime(), now)) {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
// Defensive invalid punycode check.
|
||||
if (label.startsWith(ACE_PREFIX) && label.equals(Idn.toUnicode(label))) {
|
||||
return Optional.of(makeLine(domainApplication, "Invalid punycode"));
|
||||
}
|
||||
|
||||
// Validate the SMD.
|
||||
for (EncodedSignedMark encodedSignedMark : domainApplication.getEncodedSignedMarks()) {
|
||||
byte[] signedMarkData;
|
||||
try {
|
||||
signedMarkData = encodedSignedMark.getBytes();
|
||||
} catch (IllegalStateException e) {
|
||||
return Optional.of(makeLine(domainApplication, "Incorrectly encoded SMD data"));
|
||||
}
|
||||
|
||||
SignedMark signedMark;
|
||||
try {
|
||||
signedMark = unmarshal(signedMarkData);
|
||||
} catch (EppException e) {
|
||||
return Optional.of(makeLine(domainApplication, "Unparseable SMD"));
|
||||
}
|
||||
|
||||
if (SignedMarkRevocationList.get().isSmdRevoked(signedMark.getId(),
|
||||
domainApplication.getCreationTime())) {
|
||||
return Optional.of(makeLine(domainApplication, "SMD revoked"));
|
||||
}
|
||||
|
||||
try {
|
||||
TmchXmlSignature.verify(signedMarkData);
|
||||
} catch (Exception e) {
|
||||
return Optional.of(
|
||||
makeLine(domainApplication, String.format("Invalid SMD (%s)", e.getMessage())));
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a landrush application and has no claims notice, check to see if it should have
|
||||
// one.
|
||||
if (domainApplication.getEncodedSignedMarks().isEmpty()
|
||||
&& (domainApplication.getLaunchNotice() == null
|
||||
|| domainApplication.getLaunchNotice().getNoticeId() == null
|
||||
|| isNullOrEmpty(domainApplication.getLaunchNotice().getNoticeId().getTcnId()))
|
||||
&& ClaimsListShard.get().getClaimKey(label) != null) {
|
||||
return Optional.of(makeLine(domainApplication, "Missing claims notice"));
|
||||
}
|
||||
|
||||
return Optional.of(makeLine(domainApplication, "Valid"));
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
private String makeLine(DomainApplication domainApplication, String validityMessage) {
|
||||
return Joiner.on(',').join(
|
||||
domainApplication.getRepoId(),
|
||||
domainApplication.getFullyQualifiedDomainName(),
|
||||
domainApplication.getEncodedSignedMarks().isEmpty() ? "landrush" : "sunrise",
|
||||
domainApplication.getApplicationStatus(),
|
||||
domainApplication.getCurrentSponsorClientId(),
|
||||
domainApplication.loadRegistrant().getEmailAddress(),
|
||||
validityMessage);
|
||||
}
|
||||
}
|
257
java/google/registry/tools/GenerateAuctionDataCommand.java
Normal file
257
java/google/registry/tools/GenerateAuctionDataCommand.java
Normal file
|
@ -0,0 +1,257 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.google.domain.registry.model.domain.launch.ApplicationStatus.REJECTED;
|
||||
import static com.google.domain.registry.model.domain.launch.ApplicationStatus.VALIDATED;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
import com.google.domain.registry.model.contact.ContactAddress;
|
||||
import com.google.domain.registry.model.contact.ContactPhoneNumber;
|
||||
import com.google.domain.registry.model.contact.ContactResource;
|
||||
import com.google.domain.registry.model.contact.PostalInfo;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.registrar.RegistrarAddress;
|
||||
import com.google.domain.registry.model.registrar.RegistrarContact;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/** Command to generate the auction data for a TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "Generate auction data")
|
||||
final class GenerateAuctionDataCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "TLD(s) to generate auction data for",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file.",
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Parameter(
|
||||
names = "--skip_validated_check",
|
||||
description = "Skip the check that all contended applications are already validated.")
|
||||
private boolean skipValidatedCheck;
|
||||
|
||||
/** This is the date format expected in the output file. */
|
||||
final DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
checkArgument(mainParameters.size() == 1,
|
||||
"Expected a single parameter with the TLD name. Actual: %s",
|
||||
Joiner.on(' ').join(mainParameters));
|
||||
String tld = mainParameters.get(0);
|
||||
assertTldExists(tld);
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
Set<String> registrars = new TreeSet<>();
|
||||
|
||||
for (Map.Entry<String, Collection<DomainApplication>> entry :
|
||||
getDomainApplicationMap(tld).asMap().entrySet()) {
|
||||
String domainName = entry.getKey();
|
||||
List<DomainApplication> domainApplications = filterApplications(entry.getValue());
|
||||
|
||||
// Skip the domain if there are no contentions. This can happen if there is only a single
|
||||
// sunrise applicant, or if there are no sunrise applicants and just a single landrush
|
||||
// application.
|
||||
if (domainApplications.size() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<String> emailAddresses = new HashSet<>();
|
||||
for (DomainApplication domainApplication : domainApplications) {
|
||||
checkState(skipValidatedCheck || domainApplication.getApplicationStatus() == VALIDATED, ""
|
||||
+ "Can't process contending applications for %s because some applications "
|
||||
+ "are not yet validated.", domainName);
|
||||
|
||||
ContactResource registrant = checkNotNull(domainApplication.loadRegistrant());
|
||||
result.add(emitApplication(domainApplication, registrant));
|
||||
|
||||
// Ensure the registrant's email address is unique across the contending applications.
|
||||
if (!emailAddresses.add(registrant.getEmailAddress())) {
|
||||
System.err.printf(
|
||||
"Warning: Multiple applications found with email address %s for domain %s\n",
|
||||
registrant.getEmailAddress(),
|
||||
domainName);
|
||||
}
|
||||
|
||||
// Add registrar for this application our set of registrars that we must output at the end.
|
||||
registrars.add(domainApplication.getCurrentSponsorClientId());
|
||||
}
|
||||
}
|
||||
|
||||
// Output records for the registrars of any applications we emitted above.
|
||||
for (String clientId : registrars) {
|
||||
Registrar registrar =
|
||||
checkNotNull(Registrar.loadByClientId(clientId), "Registrar %s does not exist", clientId);
|
||||
result.add(emitRegistrar(registrar));
|
||||
}
|
||||
|
||||
Files.write(output, result, UTF_8);
|
||||
}
|
||||
|
||||
/** Return a map of all fully-qualified domain names mapped to the applications for that name. */
|
||||
private static Multimap<String, DomainApplication> getDomainApplicationMap(final String tld) {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
Multimap<String, DomainApplication> domainApplicationMap = TreeMultimap.create(
|
||||
Ordering.natural(), new Comparator<DomainApplication>() {
|
||||
@Override
|
||||
public int compare(DomainApplication o1, DomainApplication o2) {
|
||||
return o1.getForeignKey().compareTo(o2.getForeignKey());
|
||||
}});
|
||||
Iterable<DomainApplication> domainApplications =
|
||||
ofy().load().type(DomainApplication.class).filter("tld", tld);
|
||||
for (DomainApplication domainApplication : domainApplications) {
|
||||
// Ignore deleted and rejected applications. They aren't under consideration.
|
||||
ApplicationStatus applicationStatus = domainApplication.getApplicationStatus();
|
||||
DateTime deletionTime = domainApplication.getDeletionTime();
|
||||
if (applicationStatus == REJECTED || isAtOrAfter(now, deletionTime)) {
|
||||
continue;
|
||||
}
|
||||
boolean result = domainApplicationMap.put(
|
||||
domainApplication.getFullyQualifiedDomainName(), domainApplication);
|
||||
checkState(result, "Domain application not added to map: %s", domainApplication);
|
||||
}
|
||||
return domainApplicationMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter applications by their priority. If there are any sunrise applications, then those will
|
||||
* be returned; otherwise just the landrush applications will be returned.
|
||||
*/
|
||||
private static List<DomainApplication> filterApplications(
|
||||
Iterable<DomainApplication> domainApplications) {
|
||||
// Sort the applications into sunrise and landrush applications.
|
||||
List<DomainApplication> sunriseApplications = new ArrayList<>();
|
||||
List<DomainApplication> landrushApplications = new ArrayList<>();
|
||||
for (DomainApplication domainApplication : domainApplications) {
|
||||
if (!domainApplication.getEncodedSignedMarks().isEmpty()) {
|
||||
sunriseApplications.add(domainApplication);
|
||||
} else {
|
||||
landrushApplications.add(domainApplication);
|
||||
}
|
||||
}
|
||||
|
||||
return !sunriseApplications.isEmpty() ? sunriseApplications : landrushApplications;
|
||||
}
|
||||
|
||||
/** Return a record line for the given application. */
|
||||
private String emitApplication(DomainApplication domainApplication, ContactResource registrant) {
|
||||
Optional<PostalInfo> postalInfo =
|
||||
Optional.fromNullable(registrant.getInternationalizedPostalInfo())
|
||||
.or(Optional.fromNullable(registrant.getLocalizedPostalInfo()));
|
||||
Optional<ContactAddress> address =
|
||||
Optional.fromNullable(postalInfo.isPresent() ? postalInfo.get().getAddress() : null);
|
||||
List<String> street =
|
||||
address.isPresent() ? address.get().getStreet() : ImmutableList.<String>of();
|
||||
Optional<ContactPhoneNumber> phoneNumber = Optional.fromNullable(registrant.getVoiceNumber());
|
||||
|
||||
// Each line containing an auction participant has the following format:
|
||||
//
|
||||
// Domain|Application ID|Application timestamp|Last update date|Registrar Name|
|
||||
// Registrant Name|Registrant Company|Registrant Address 1|Registrant Address 2|
|
||||
// Registrant City|Registrant Province|Registrant Postal Code|Registrant Country|
|
||||
// Registrant Email|Registrant Telephone|Reserve|Application Type
|
||||
return Joiner.on('|').join(ImmutableList.of(
|
||||
domainApplication.getFullyQualifiedDomainName(),
|
||||
domainApplication.getForeignKey(),
|
||||
formatter.print(domainApplication.getCreationTime()),
|
||||
domainApplication.getLastEppUpdateTime() != null
|
||||
? formatter.print(domainApplication.getLastEppUpdateTime()) : "",
|
||||
domainApplication.getCurrentSponsorClientId(),
|
||||
nullToEmpty(postalInfo.isPresent() ? postalInfo.get().getName() : ""),
|
||||
nullToEmpty(postalInfo.isPresent() ? postalInfo.get().getOrg() : ""),
|
||||
Iterables.getFirst(street, ""),
|
||||
Joiner.on(' ').skipNulls().join(Iterables.skip(street, 1)),
|
||||
nullToEmpty(address.isPresent() ? address.get().getCity() : ""),
|
||||
nullToEmpty(address.isPresent() ? address.get().getState() : ""),
|
||||
nullToEmpty(address.isPresent() ? address.get().getZip() : ""),
|
||||
nullToEmpty(address.isPresent() ? address.get().getCountryCode() : ""),
|
||||
nullToEmpty(registrant.getEmailAddress()),
|
||||
nullToEmpty(phoneNumber.isPresent() ? phoneNumber.get().toPhoneString() : ""),
|
||||
"",
|
||||
domainApplication.getEncodedSignedMarks().isEmpty() ? "Landrush" : "Sunrise"));
|
||||
}
|
||||
|
||||
/** Return a record line for the given registrar. */
|
||||
private static String emitRegistrar(Registrar registrar) {
|
||||
// TODO(b/19016140): Determine if this set-up is required.
|
||||
Optional<RegistrarContact> contact =
|
||||
Optional.fromNullable(Iterables.getFirst(registrar.getContacts(), null));
|
||||
Optional<RegistrarAddress> address = Optional.fromNullable(registrar.getLocalizedAddress())
|
||||
.or(Optional.fromNullable(registrar.getInternationalizedAddress()));
|
||||
List<String> street =
|
||||
address.isPresent() ? address.get().getStreet() : ImmutableList.<String>of();
|
||||
|
||||
// Each line containing the registrar of an auction participant has the following format:
|
||||
//
|
||||
// Registrar Name|Registrar Contact Name|Registrar Full Company|Registrar Address 1|
|
||||
// Registrar Address 2|Registrar City|Registrar Province|Registrar Postal Code|
|
||||
// Registrar Country|Registrar Email|Registrar Telephone
|
||||
return Joiner.on('|').join(ImmutableList.of(
|
||||
registrar.getClientIdentifier(),
|
||||
contact.isPresent() ? contact.get().getName() : "N/A",
|
||||
nullToEmpty(registrar.getRegistrarName()),
|
||||
Iterables.getFirst(street, ""),
|
||||
Iterables.get(street, 1, ""),
|
||||
address.isPresent() ? nullToEmpty(address.get().getCity()) : "",
|
||||
address.isPresent() ? nullToEmpty(address.get().getState()) : "",
|
||||
address.isPresent() ? nullToEmpty(address.get().getZip()) : "",
|
||||
address.isPresent() ? nullToEmpty(address.get().getCountryCode()) : "",
|
||||
nullToEmpty(registrar.getEmailAddress()),
|
||||
nullToEmpty(registrar.getPhoneNumber())));
|
||||
}
|
||||
}
|
158
java/google/registry/tools/GenerateDnsReportCommand.java
Normal file
158
java/google/registry/tools/GenerateDnsReportCommand.java
Normal file
|
@ -0,0 +1,158 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isBeforeOrAt;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
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.Ordering;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to generate a report of all DNS data. */
|
||||
@Parameters(separators = " =", commandDescription = "Generate report of all DNS data in a TLD.")
|
||||
final class GenerateDnsReportCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "Target TLD.",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file.",
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
assertTldExists(tld);
|
||||
Files.write(output, new Generator().generate().getBytes(US_ASCII));
|
||||
}
|
||||
|
||||
private class Generator {
|
||||
private final DateTime now = clock.nowUtc();
|
||||
private final StringBuilder result = new StringBuilder();
|
||||
private boolean first = true;
|
||||
|
||||
String generate() {
|
||||
result.append("[\n");
|
||||
|
||||
Iterable<DomainResource> domains = ofy().load().type(DomainResource.class).filter("tld", tld);
|
||||
for (DomainResource domain : domains) {
|
||||
// Skip deleted domains and domains that don't get published to DNS.
|
||||
if (isBeforeOrAt(domain.getDeletionTime(), now) || !domain.shouldPublishToDns()) {
|
||||
continue;
|
||||
}
|
||||
write(domain);
|
||||
}
|
||||
|
||||
Iterable<HostResource> nameservers = ofy().load().type(HostResource.class);
|
||||
for (HostResource nameserver : nameservers) {
|
||||
// Skip deleted hosts and external hosts.
|
||||
if (isBeforeOrAt(nameserver.getDeletionTime(), now)
|
||||
|| nameserver.getInetAddresses().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
write(nameserver);
|
||||
}
|
||||
|
||||
return result.append("\n]\n").toString();
|
||||
}
|
||||
|
||||
private void write(DomainResource domain) {
|
||||
ImmutableList<String> nameservers = FluentIterable.from(domain.loadNameservers())
|
||||
.transform(new Function<HostResource, String>() {
|
||||
@Override
|
||||
public String apply(HostResource host) {
|
||||
return host.getForeignKey();
|
||||
}})
|
||||
.toSortedList(Ordering.natural());
|
||||
ImmutableList<Map<String, ?>> dsData = FluentIterable.from(domain.getDsData())
|
||||
.transform(new Function<DelegationSignerData, Map<String, ?>>() {
|
||||
@Override
|
||||
public Map<String, ?> apply(DelegationSignerData dsData) {
|
||||
return ImmutableMap.of(
|
||||
"keyTag", dsData.getKeyTag(),
|
||||
"algorithm", dsData.getAlgorithm(),
|
||||
"digestType", dsData.getDigestType(),
|
||||
"digest", base16().encode(dsData.getDigest()));
|
||||
}})
|
||||
.toList();
|
||||
ImmutableMap.Builder<String, Object> mapBuilder = new ImmutableMap.Builder<>();
|
||||
mapBuilder.put("domain", domain.getFullyQualifiedDomainName());
|
||||
if (!nameservers.isEmpty()) {
|
||||
mapBuilder.put("nameservers", nameservers);
|
||||
}
|
||||
if (!dsData.isEmpty()) {
|
||||
mapBuilder.put("dsData", dsData);
|
||||
}
|
||||
writeJson(mapBuilder.build());
|
||||
}
|
||||
|
||||
private void write(HostResource nameserver) {
|
||||
ImmutableList<String> ipAddresses = FluentIterable.from(nameserver.getInetAddresses())
|
||||
.transform(new Function<InetAddress, String>() {
|
||||
@Override
|
||||
public String apply(InetAddress inetAddress) {
|
||||
return inetAddress.getHostAddress();
|
||||
}})
|
||||
.toSortedList(Ordering.natural());
|
||||
ImmutableMap<String, ?> map = ImmutableMap.of(
|
||||
"host", nameserver.getFullyQualifiedHostName(),
|
||||
"ips", ipAddresses);
|
||||
writeJson(map);
|
||||
}
|
||||
|
||||
private void writeJson(Map<String, ?> map) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
result.append(",\n");
|
||||
}
|
||||
result.append(JSONValue.toJSONString(map));
|
||||
}
|
||||
}
|
||||
}
|
243
java/google/registry/tools/GenerateEscrowDepositCommand.java
Normal file
243
java/google/registry/tools/GenerateEscrowDepositCommand.java
Normal file
|
@ -0,0 +1,243 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.model.EppResourceUtils;
|
||||
import com.google.domain.registry.model.ImmutableObject;
|
||||
import com.google.domain.registry.model.contact.ContactResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.model.index.EppResourceIndex;
|
||||
import com.google.domain.registry.model.index.EppResourceIndexBucket;
|
||||
import com.google.domain.registry.model.rde.RdeMode;
|
||||
import com.google.domain.registry.model.rde.RdeNamingUtils;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.rde.DepositFragment;
|
||||
import com.google.domain.registry.rde.RdeCounter;
|
||||
import com.google.domain.registry.rde.RdeMarshaller;
|
||||
import com.google.domain.registry.rde.RdeResourceType;
|
||||
import com.google.domain.registry.rde.RdeUtil;
|
||||
import com.google.domain.registry.tldconfig.idn.IdnTableEnum;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.DateTimeParameter;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
import com.google.domain.registry.xjc.rdeheader.XjcRdeHeader;
|
||||
import com.google.domain.registry.xjc.rdeheader.XjcRdeHeaderElement;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.Result;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to generate an XML RDE escrow deposit (with relevant files) in current directory. */
|
||||
@Parameters(separators = " =", commandDescription = "Generate an XML escrow deposit.")
|
||||
final class GenerateEscrowDepositCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "Top level domain for which deposit should be generated.",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = {"-w", "--watermark"},
|
||||
description = "Point-in-time timestamp for snapshotting the datastore.",
|
||||
validateWith = DateTimeParameter.class)
|
||||
private DateTime watermark = DateTime.now(UTC);
|
||||
|
||||
@Parameter(
|
||||
names = {"-m", "--mode"},
|
||||
description = "RDE/BRDA mode of operation.")
|
||||
private RdeMode mode = RdeMode.FULL;
|
||||
|
||||
@Parameter(
|
||||
names = {"-r", "--revision"},
|
||||
description = "Revision number. Use >0 for resends.")
|
||||
private int revision = 0;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--outdir"},
|
||||
description = "Specify output directory. Default is current directory.",
|
||||
validateWith = PathParameter.OutputDirectory.class)
|
||||
private Path outdir = Paths.get(".");
|
||||
|
||||
@Inject
|
||||
EscrowDepositEncryptor encryptor;
|
||||
|
||||
@Inject
|
||||
RdeCounter counter;
|
||||
|
||||
@Inject
|
||||
@Config("eppResourceIndexBucketCount")
|
||||
int eppResourceIndexBucketCount;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
RdeMarshaller marshaller = new RdeMarshaller();
|
||||
assertTldExists(tld);
|
||||
String suffix = String.format("-%s-%s.tmp.xml", tld, watermark);
|
||||
Path xmlPath = outdir.resolve("deposit" + suffix);
|
||||
Path reportPath = outdir.resolve("report" + suffix);
|
||||
try {
|
||||
String id = RdeUtil.timestampToId(watermark);
|
||||
XjcRdeHeader header;
|
||||
try (OutputStream xmlOutputBytes = Files.newOutputStream(xmlPath);
|
||||
Writer xmlOutput = new OutputStreamWriter(xmlOutputBytes, UTF_8)) {
|
||||
xmlOutput.write(
|
||||
marshaller.makeHeader(id, watermark, RdeResourceType.getUris(mode), revision));
|
||||
for (ImmutableObject resource
|
||||
: Iterables.concat(Registrar.loadAll(), load(scan()))) {
|
||||
DepositFragment frag;
|
||||
if (resource instanceof Registrar) {
|
||||
frag = marshaller.marshalRegistrar((Registrar) resource);
|
||||
} else if (resource instanceof ContactResource) {
|
||||
frag = marshaller.marshalContact((ContactResource) resource);
|
||||
} else if (resource instanceof DomainResource) {
|
||||
DomainResource domain = (DomainResource) resource;
|
||||
if (!domain.getTld().equals(tld)) {
|
||||
continue;
|
||||
}
|
||||
frag = marshaller.marshalDomain(domain, mode);
|
||||
} else if (resource instanceof HostResource) {
|
||||
frag = marshaller.marshalHost((HostResource) resource);
|
||||
} else {
|
||||
continue; // Surprise polymorphic entities, e.g. DomainApplication.
|
||||
}
|
||||
if (!frag.xml().isEmpty()) {
|
||||
xmlOutput.write(frag.xml());
|
||||
counter.increment(frag.type());
|
||||
}
|
||||
if (!frag.error().isEmpty()) {
|
||||
System.err.print(frag.error());
|
||||
}
|
||||
}
|
||||
for (IdnTableEnum idn : IdnTableEnum.values()) {
|
||||
xmlOutput.write(marshaller.marshalIdn(idn.getTable()));
|
||||
counter.increment(RdeResourceType.IDN);
|
||||
}
|
||||
header = counter.makeHeader(tld, mode);
|
||||
xmlOutput.write(marshaller.marshalStrictlyOrDie(new XjcRdeHeaderElement(header)));
|
||||
xmlOutput.write(marshaller.makeFooter());
|
||||
}
|
||||
try (OutputStream reportOutputBytes = Files.newOutputStream(reportPath)) {
|
||||
counter.makeReport(id, watermark, header, revision).marshal(reportOutputBytes, UTF_8);
|
||||
}
|
||||
String name = RdeNamingUtils.makeRydeFilename(tld, watermark, mode, 1, revision);
|
||||
encryptor.encrypt(tld, xmlPath, outdir);
|
||||
Files.move(xmlPath, outdir.resolve(name + ".xml"), REPLACE_EXISTING);
|
||||
Files.move(reportPath, outdir.resolve(name + "-report.xml"), REPLACE_EXISTING);
|
||||
} finally {
|
||||
Files.deleteIfExists(xmlPath);
|
||||
Files.deleteIfExists(reportPath);
|
||||
}
|
||||
}
|
||||
|
||||
private Iterable<EppResource> scan() {
|
||||
return Iterables.concat(
|
||||
Iterables.transform(
|
||||
getEppResourceIndexBuckets(),
|
||||
new Function<Key<EppResourceIndexBucket>, Iterable<EppResource>>() {
|
||||
@Override
|
||||
public Iterable<EppResource> apply(Key<EppResourceIndexBucket> bucket) {
|
||||
System.err.printf("Scanning EppResourceIndexBucket %d of %d...\n",
|
||||
bucket.getId(), eppResourceIndexBucketCount);
|
||||
return scanBucket(bucket);
|
||||
}}));
|
||||
}
|
||||
|
||||
private Iterable<EppResource> scanBucket(final Key<EppResourceIndexBucket> bucket) {
|
||||
return ofy().load()
|
||||
.keys(FluentIterable
|
||||
.from(mode == RdeMode.FULL
|
||||
? Arrays.asList(
|
||||
Key.getKind(ContactResource.class),
|
||||
Key.getKind(DomainResource.class),
|
||||
Key.getKind(HostResource.class))
|
||||
: Arrays.asList(
|
||||
Key.getKind(DomainResource.class)))
|
||||
.transformAndConcat(new Function<String, Iterable<EppResourceIndex>>() {
|
||||
@Override
|
||||
public Iterable<EppResourceIndex> apply(String kind) {
|
||||
return ofy().load()
|
||||
.type(EppResourceIndex.class)
|
||||
.ancestor(bucket)
|
||||
.filter("kind", kind)
|
||||
.iterable();
|
||||
}})
|
||||
.transform(new Function<EppResourceIndex, Key<EppResource>>() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Key<EppResource> apply(EppResourceIndex index) {
|
||||
return (Key<EppResource>) index.getReference().getKey();
|
||||
}}))
|
||||
.values();
|
||||
}
|
||||
|
||||
private <T extends EppResource> Iterable<T> load(final Iterable<T> resources) {
|
||||
return FluentIterable
|
||||
.from(Iterables.partition(
|
||||
Iterables.transform(resources,
|
||||
new Function<T, Result<T>>() {
|
||||
@Override
|
||||
public Result<T> apply(T resource) {
|
||||
return EppResourceUtils.loadAtPointInTime(resource, watermark);
|
||||
}}),
|
||||
1000))
|
||||
.transformAndConcat(new Function<Iterable<Result<T>>, Iterable<T>>() {
|
||||
@Override
|
||||
public Iterable<T> apply(Iterable<Result<T>> results) {
|
||||
return Iterables.transform(results,
|
||||
new Function<Result<T>, T>() {
|
||||
@Override
|
||||
public T apply(Result<T> result) {
|
||||
return result.now();
|
||||
}});
|
||||
}})
|
||||
.filter(Predicates.notNull());
|
||||
}
|
||||
|
||||
private ImmutableList<Key<EppResourceIndexBucket>> getEppResourceIndexBuckets() {
|
||||
ImmutableList.Builder<Key<EppResourceIndexBucket>> builder = new ImmutableList.Builder<>();
|
||||
for (int i = 1; i <= eppResourceIndexBucketCount; i++) {
|
||||
builder.add(Key.create(EppResourceIndexBucket.class, i));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
91
java/google/registry/tools/GenerateLordnCommand.java
Normal file
91
java/google/registry/tools/GenerateLordnCommand.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;
|
||||
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.tmch.LordnTask;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/** Command to generate a LORDN CSV file for an entire TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "Generate LORDN CSV file")
|
||||
final class GenerateLordnCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "TLD to generate LORDN for",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--claims_file"},
|
||||
description = "Claims CSV output file.",
|
||||
validateWith = PathParameter.OutputFile.class,
|
||||
required = true)
|
||||
private Path claimsOutputPath;
|
||||
|
||||
@Parameter(
|
||||
names = {"-s", "--sunrise_file"},
|
||||
description = "Sunrise CSV output file.",
|
||||
validateWith = PathParameter.OutputFile.class,
|
||||
required = true)
|
||||
private Path sunriseOutputPath;
|
||||
|
||||
@Override
|
||||
public void run() throws IOException {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
ImmutableList.Builder<String> claimsCsv = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<String> sunriseCsv = new ImmutableList.Builder<>();
|
||||
for (DomainResource domain : ofy().load().type(DomainResource.class).filter("tld", tld)) {
|
||||
String status = " ";
|
||||
if (domain.getLaunchNotice() == null && domain.getSmdId() != null) {
|
||||
sunriseCsv.add(LordnTask.getCsvLineForSunriseDomain(domain, domain.getCreationTime()));
|
||||
status = "S";
|
||||
} else if (domain.getLaunchNotice() != null || domain.getSmdId() != null) {
|
||||
claimsCsv.add(LordnTask.getCsvLineForClaimsDomain(domain, domain.getCreationTime()));
|
||||
status = "C";
|
||||
}
|
||||
System.out.printf("%s[%s] ", domain.getFullyQualifiedDomainName(), status);
|
||||
}
|
||||
ImmutableList<String> claimsRows = claimsCsv.build();
|
||||
ImmutableList<String> claimsAll = new ImmutableList.Builder<String>()
|
||||
.add(String.format("1,%s,%d", now, claimsRows.size()))
|
||||
.add(LordnTask.COLUMNS_CLAIMS)
|
||||
.addAll(claimsRows)
|
||||
.build();
|
||||
ImmutableList<String> sunriseRows = sunriseCsv.build();
|
||||
ImmutableList<String> sunriseAll = new ImmutableList.Builder<String>()
|
||||
.add(String.format("1,%s,%d", now.plusMillis(1), sunriseRows.size()))
|
||||
.add(LordnTask.COLUMNS_SUNRISE)
|
||||
.addAll(sunriseRows)
|
||||
.build();
|
||||
Files.write(claimsOutputPath, claimsAll, UTF_8);
|
||||
Files.write(sunriseOutputPath, sunriseAll, UTF_8);
|
||||
}
|
||||
}
|
77
java/google/registry/tools/GenerateZoneFilesCommand.java
Normal file
77
java/google/registry/tools/GenerateZoneFilesCommand.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.joda.time.Duration.standardMinutes;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.tools.params.DateTimeParameter;
|
||||
import com.google.domain.registry.tools.server.GenerateZoneFilesAction;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Command to generate zone files. */
|
||||
@Parameters(separators = " =", commandDescription = "Generate zone files")
|
||||
final class GenerateZoneFilesCommand implements ServerSideCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "A comma-separated list of TLD to generate zone files for",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
// Default to latest midnight that's at least 2 minutes ago.
|
||||
@Parameter(
|
||||
names = "--export_time",
|
||||
description = "The (midnight UTC) time to generate the file for (defaults to last midnight).",
|
||||
validateWith = DateTimeParameter.class)
|
||||
private DateTime exportTime = DateTime.now(UTC).minus(standardMinutes(2)).withTimeAtStartOfDay();
|
||||
|
||||
private Connection connection;
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws IOException {
|
||||
for (String tld : mainParameters) {
|
||||
assertTldExists(tld);
|
||||
}
|
||||
ImmutableMap<String, Object> params = ImmutableMap.of(
|
||||
"tlds", mainParameters,
|
||||
"exportTime", exportTime.toString());
|
||||
Map<String, Object> response = connection.sendJson(GenerateZoneFilesAction.PATH, params);
|
||||
System.out.printf(
|
||||
"Job started at %s%s\n",
|
||||
connection.getServerUrl(),
|
||||
response.get("jobPath"));
|
||||
System.out.println("Output files:");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> filenames = (List<String>) response.get("filenames");
|
||||
for (String filename : filenames) {
|
||||
System.out.println(filename);
|
||||
}
|
||||
}
|
||||
}
|
41
java/google/registry/tools/GetApplicationCommand.java
Normal file
41
java/google/registry/tools/GetApplicationCommand.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a domain application. */
|
||||
@Parameters(separators = " =", commandDescription = "Show domain application record(s)")
|
||||
final class GetApplicationCommand extends GetEppResourceCommand<DomainApplication>
|
||||
implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Domain application id(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void processParameters() {
|
||||
for (String applicationId : mainParameters) {
|
||||
printResource(applicationId);
|
||||
}
|
||||
}
|
||||
}
|
73
java/google/registry/tools/GetApplicationIdsCommand.java
Normal file
73
java/google/registry/tools/GetApplicationIdsCommand.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.index.DomainApplicationIndex.loadActiveApplicationsByDomainName;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.model.registry.Registries.findTldForNameOrThrow;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to generate a list of all applications for a given domain name(s). */
|
||||
@Parameters(separators = " =",
|
||||
commandDescription = "Generate list of application IDs and sponsors for given domain name(s)")
|
||||
final class GetApplicationIdsCommand extends GetEppResourceCommand<DomainApplication>
|
||||
implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Fully qualified domain name(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
void processParameters() {
|
||||
// Note that this function explicitly performs its own resource load and print, ignoring
|
||||
// GetEppResourceCommand.printResource(), because we do NOT want to display the entire
|
||||
// application to gTech users -- just the ID# and sponsor.
|
||||
for (String domainName : mainParameters) {
|
||||
InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(domainName));
|
||||
assertTldExists(tld.toString());
|
||||
System.out.printf("%s:%n", domainName);
|
||||
|
||||
// Sample output:
|
||||
// example.tld:
|
||||
// 1 (NewRegistrar)
|
||||
// 2 (OtherRegistrar)
|
||||
// example2.tld:
|
||||
// No applications exist for 'example2.tld'.
|
||||
ImmutableList<DomainApplication> applications = ImmutableList.copyOf(
|
||||
loadActiveApplicationsByDomainName(domainName, readTimestamp));
|
||||
if (applications.isEmpty()) {
|
||||
System.out.printf(" No applications exist for \'%s\'.%n", domainName);
|
||||
} else {
|
||||
for (DomainApplication application : applications) {
|
||||
System.out.printf(
|
||||
" %s (%s)%n",
|
||||
application.getForeignKey(),
|
||||
application.getCurrentSponsorClientId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
96
java/google/registry/tools/GetAppliedLabelsCommand.java
Normal file
96
java/google/registry/tools/GetAppliedLabelsCommand.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.domain.launch.ApplicationStatus.REJECTED;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
import com.google.domain.registry.util.Idn;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.beust.jcommander.internal.Sets;
|
||||
import com.googlecode.objectify.Work;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** Command to generate a list of all slds in a tld that have open applications. */
|
||||
@Parameters(separators = " =", commandDescription = "Generate applied-for domains list")
|
||||
final class GetAppliedLabelsCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "TLD to generate list for.",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file.",
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
List<String> lines = new ArrayList<>();
|
||||
for (String label : getDomainApplicationMap(assertTldExists(tld))) {
|
||||
label = label.substring(0, label.lastIndexOf('.'));
|
||||
try {
|
||||
lines.add(Idn.toUnicode(label.toLowerCase()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// An invalid punycode label that we need to reject later.
|
||||
lines.add(label + " (invalid)");
|
||||
}
|
||||
}
|
||||
Files.write(output, lines, UTF_8);
|
||||
}
|
||||
|
||||
/** Return a set of all fully-qualified domain names with open applications. */
|
||||
private static Set<String> getDomainApplicationMap(final String tld) {
|
||||
return ofy().transact(new Work<Set<String>>() {
|
||||
@Override
|
||||
public Set<String> run() {
|
||||
Set<String> labels = Sets.newHashSet();
|
||||
List<DomainApplication> domainApplications;
|
||||
domainApplications = ofy().load().type(DomainApplication.class).filter("tld", tld).list();
|
||||
for (DomainApplication domainApplication : domainApplications) {
|
||||
// Ignore deleted and rejected applications. They aren't under consideration.
|
||||
ApplicationStatus applicationStatus = domainApplication.getApplicationStatus();
|
||||
DateTime deletionTime = domainApplication.getDeletionTime();
|
||||
if (applicationStatus == REJECTED
|
||||
|| isAtOrAfter(ofy().getTransactionTime(), deletionTime)) {
|
||||
continue;
|
||||
}
|
||||
labels.add(domainApplication.getFullyQualifiedDomainName());
|
||||
}
|
||||
return labels;
|
||||
}});
|
||||
}
|
||||
}
|
53
java/google/registry/tools/GetClaimsListCommand.java
Normal file
53
java/google/registry/tools/GetClaimsListCommand.java
Normal file
|
@ -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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.domain.registry.model.tmch.ClaimsListShard;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* A command to download the current claims list.
|
||||
*
|
||||
* <p>This is not the original file we fetched from TMCH. It is just a representation of what we
|
||||
* are currently storing in datastore.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Download the current claims list")
|
||||
final class GetClaimsListCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file.",
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
ClaimsListShard cl = checkNotNull(ClaimsListShard.get(), "Couldn't load ClaimsList");
|
||||
String csv = Joiner.on('\n').withKeyValueSeparator(",").join(cl.getLabelsToKeys()) + "\n";
|
||||
Files.write(csv, output.toFile(), UTF_8);
|
||||
}
|
||||
}
|
41
java/google/registry/tools/GetContactCommand.java
Normal file
41
java/google/registry/tools/GetContactCommand.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.model.contact.ContactResource;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show one or more contacts. */
|
||||
@Parameters(separators = " =", commandDescription = "Show contact record(s)")
|
||||
final class GetContactCommand extends GetEppResourceCommand<ContactResource>
|
||||
implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Contact id(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void processParameters() {
|
||||
for (String contactNameString : mainParameters) {
|
||||
printResource(contactNameString);
|
||||
}
|
||||
}
|
||||
}
|
41
java/google/registry/tools/GetDomainCommand.java
Normal file
41
java/google/registry/tools/GetDomainCommand.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a domain resource. */
|
||||
@Parameters(separators = " =", commandDescription = "Show domain record(s)")
|
||||
final class GetDomainCommand extends GetEppResourceCommand<DomainResource>
|
||||
implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Fully qualified domain name(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void processParameters() {
|
||||
for (String domainName : mainParameters) {
|
||||
printResource(domainName);
|
||||
}
|
||||
}
|
||||
}
|
78
java/google/registry/tools/GetEppResourceCommand.java
Normal file
78
java/google/registry/tools/GetEppResourceCommand.java
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.util.TypeUtils.TypeInstantiator;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Abstract command to show a resource.
|
||||
*
|
||||
* @param <R> {@link EppResource} subclass.
|
||||
*/
|
||||
@Parameters(separators = " =")
|
||||
abstract class GetEppResourceCommand<R extends EppResource>
|
||||
implements RemoteApiCommand {
|
||||
|
||||
private final DateTime now = DateTime.now(UTC);
|
||||
|
||||
private Class<R> clazz = new TypeInstantiator<R>(getClass()){}.getExactType();
|
||||
|
||||
@Parameter(
|
||||
names = "--read_timestamp",
|
||||
description = "Timestamp to use when reading. May not be in the past.")
|
||||
protected DateTime readTimestamp = now;
|
||||
|
||||
@Parameter(
|
||||
names = "--expand",
|
||||
description = "Fully expand the requested resource. NOTE: Output may be lengthy.")
|
||||
boolean expand;
|
||||
|
||||
/** Resolve any parameters into ids for loadResource. */
|
||||
abstract void processParameters();
|
||||
|
||||
/**
|
||||
* Load a resource by ID and output. Append the websafe key to the output for use in e.g.
|
||||
* manual mapreduce calls.
|
||||
*/
|
||||
void printResource(String uniqueId) {
|
||||
R resource = loadByUniqueId(clazz, uniqueId, readTimestamp);
|
||||
System.out.println(resource != null
|
||||
? String.format("%s\n\nWebsafe key: %s",
|
||||
expand ? resource.toHydratedString() : resource,
|
||||
Key.create(resource).getString())
|
||||
: String.format(
|
||||
"%s '%s' does not exist or is deleted\n",
|
||||
clazz.getSimpleName().replace("Resource", ""),
|
||||
uniqueId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
checkArgument(!readTimestamp.isBefore(now), "--read_timestamp may not be in the past");
|
||||
processParameters();
|
||||
}
|
||||
}
|
63
java/google/registry/tools/GetHistoryEntriesCommand.java
Normal file
63
java/google/registry/tools/GetHistoryEntriesCommand.java
Normal file
|
@ -0,0 +1,63 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static com.google.domain.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.xml.XmlTransformer;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command to show history entries. */
|
||||
@Parameters(separators = " =",
|
||||
commandDescription = "Show history entries that occurred in a given time range")
|
||||
final class GetHistoryEntriesCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-a", "--after"},
|
||||
description = "Only show history entries that occurred at or after this time")
|
||||
private DateTime after = START_OF_TIME;
|
||||
|
||||
@Parameter(
|
||||
names = {"-b", "--before"},
|
||||
description = "Only show history entries that occurred at or before this time")
|
||||
private DateTime before = END_OF_TIME;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (HistoryEntry entry : ofy().load().type(HistoryEntry.class)
|
||||
.order("modificationTime")
|
||||
.filter("modificationTime >=", after)
|
||||
.filter("modificationTime <=", before)) {
|
||||
System.out.printf(
|
||||
"Client: %s\nTime: %s\nClient TRID: %s\nServer TRID: %s\n%s\n",
|
||||
entry.getClientId(),
|
||||
entry.getModificationTime(),
|
||||
entry.getTrid().getClientTransactionId(),
|
||||
entry.getTrid().getServerTransactionId(),
|
||||
entry.getXmlBytes() == null
|
||||
? String.format("[no XML stored for %s]\n", entry.getType())
|
||||
: XmlTransformer.prettyPrint(entry.getXmlBytes()));
|
||||
}
|
||||
}
|
||||
}
|
41
java/google/registry/tools/GetHostCommand.java
Normal file
41
java/google/registry/tools/GetHostCommand.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show one or more host resources. */
|
||||
@Parameters(separators = " =", commandDescription = "Show host record(s)")
|
||||
final class GetHostCommand extends GetEppResourceCommand<HostResource>
|
||||
implements GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Fully qualified host name(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void processParameters() {
|
||||
for (String hostName : mainParameters) {
|
||||
printResource(hostName);
|
||||
}
|
||||
}
|
||||
}
|
46
java/google/registry/tools/GetRegistrarCommand.java
Normal file
46
java/google/registry/tools/GetRegistrarCommand.java
Normal file
|
@ -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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a registrar record. */
|
||||
@Parameters(separators = " =", commandDescription = "Show registrar record(s)")
|
||||
final class GetRegistrarCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Client identifier of the registrar account(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (String clientIdentifier : mainParameters) {
|
||||
Registrar registrar = Registrar.loadByClientId(clientIdentifier);
|
||||
checkState(registrar != null, "Registrar does not exist");
|
||||
|
||||
System.out.println(registrar);
|
||||
}
|
||||
}
|
||||
}
|
56
java/google/registry/tools/GetResourceByKeyCommand.java
Normal file
56
java/google/registry/tools/GetResourceByKeyCommand.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Command to get info on a datastore resource by websafe key.
|
||||
*/
|
||||
@Parameters(separators = " =")
|
||||
final class GetResourceByKeyCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "Websafe key string(s)",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Parameter(
|
||||
names = "--expand",
|
||||
description = "Fully expand the requested resource. NOTE: Output may be lengthy.")
|
||||
boolean expand;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (String keyString : mainParameters) {
|
||||
System.out.println("\n");
|
||||
Key<EppResource> resourceKey = checkNotNull(
|
||||
Key.<EppResource>create(keyString), "Could not parse key string: " + keyString);
|
||||
EppResource resource = checkNotNull(
|
||||
ofy().load().key(resourceKey).now(), "Could not load resource for key: " + resourceKey);
|
||||
System.out.println(expand ? resource.toHydratedString() : resource.toString());
|
||||
}
|
||||
}
|
||||
}
|
29
java/google/registry/tools/GetSchemaCommand.java
Normal file
29
java/google/registry/tools/GetSchemaCommand.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.model.SchemaVersion;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Generates the schema file used for model versioning. */
|
||||
@Parameters(commandDescription = "Generate a model schema file")
|
||||
final class GetSchemaCommand implements GtechCommand {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
System.out.println(SchemaVersion.getSchema());
|
||||
}
|
||||
}
|
167
java/google/registry/tools/GetSchemaTreeCommand.java
Normal file
167
java/google/registry/tools/GetSchemaTreeCommand.java
Normal file
|
@ -0,0 +1,167 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.collect.Ordering.arbitrary;
|
||||
import static com.google.domain.registry.model.EntityClasses.ALL_CLASSES;
|
||||
import static java.lang.ClassLoader.getSystemClassLoader;
|
||||
import static java.lang.reflect.Modifier.isAbstract;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
import com.google.domain.registry.model.BackupGroupRoot;
|
||||
import com.google.domain.registry.model.annotations.NotBackedUp;
|
||||
import com.google.domain.registry.model.annotations.VirtualEntity;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.annotation.Entity;
|
||||
import com.googlecode.objectify.annotation.EntitySubclass;
|
||||
import com.googlecode.objectify.annotation.Parent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Visualizes the schema parentage tree. */
|
||||
@Parameters(commandDescription = "Generate a model schema file")
|
||||
final class GetSchemaTreeCommand implements GtechCommand {
|
||||
|
||||
/** Mapping from parent classes in the Datastore sense to child classes. */
|
||||
private final Multimap<Class<?>, Class<?>> hierarchy =
|
||||
TreeMultimap.create(arbitrary(), new PrintableNameOrdering());
|
||||
|
||||
/** Mapping from superclasses used in parentage to concrete subclasses. */
|
||||
private Multimap<Class<?>, Class<?>> superclassToSubclasses;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
// Get the @Parent type for each class.
|
||||
Map<Class<?>, Class<?>> entityToParentType = new HashMap<>();
|
||||
for (Class<?> clazz : ALL_CLASSES) {
|
||||
entityToParentType.put(clazz, getParentType(clazz));
|
||||
}
|
||||
// Find super types like EppResource that are used as parents in place of actual entity types.
|
||||
Set<Class<?>> superclasses = new HashSet<>();
|
||||
for (Class<?> clazz : ALL_CLASSES) {
|
||||
Class<?> parentType = entityToParentType.get(clazz);
|
||||
if (!ALL_CLASSES.contains(parentType) && !Object.class.equals(parentType)) {
|
||||
superclasses.add(parentType);
|
||||
}
|
||||
}
|
||||
// Find the subclasses for each superclass we just found, and map them to their superclasses.
|
||||
Map<Class<?>, Class<?>> subclassToSuperclass = new HashMap<>();
|
||||
for (Class<?> clazz : ALL_CLASSES) {
|
||||
for (Class<?> superclass : superclasses) {
|
||||
if (superclass.isAssignableFrom(clazz)) {
|
||||
subclassToSuperclass.put(clazz, superclass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Map @EntitySubclass classes to their superclasses.
|
||||
for (Class<?> clazz : ALL_CLASSES) {
|
||||
if (clazz.isAnnotationPresent(EntitySubclass.class)) {
|
||||
Class<?> entityClass = clazz;
|
||||
while (!entityClass.isAnnotationPresent(Entity.class)) {
|
||||
entityClass = entityClass.getSuperclass();
|
||||
}
|
||||
if (subclassToSuperclass.containsKey(clazz)) {
|
||||
subclassToSuperclass.put(entityClass, subclassToSuperclass.get(clazz));
|
||||
}
|
||||
subclassToSuperclass.put(clazz, entityClass);
|
||||
}
|
||||
}
|
||||
// Build the parentage hierarchy, replacing subclasses with superclasses wherever possible.
|
||||
for (Class<?> clazz : ALL_CLASSES) {
|
||||
Class<?> superclass = clazz;
|
||||
while (subclassToSuperclass.containsKey(superclass)) {
|
||||
superclass = subclassToSuperclass.get(superclass);
|
||||
}
|
||||
hierarchy.put(entityToParentType.get(clazz), superclass == null ? clazz : superclass);
|
||||
}
|
||||
// Build up the superclass to subclass mapping.
|
||||
superclassToSubclasses = Multimaps.invertFrom(
|
||||
Multimaps.forMap(subclassToSuperclass),
|
||||
TreeMultimap.<Class<?>, Class<?>>create(arbitrary(), new PrintableNameOrdering()));
|
||||
printTree(Object.class, 0);
|
||||
}
|
||||
|
||||
private Class<?> getParentType(Class<?> clazz) {
|
||||
for (; clazz != null; clazz = clazz.getSuperclass()) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(Parent.class)) {
|
||||
try {
|
||||
return getSystemClassLoader().loadClass(
|
||||
((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]
|
||||
.toString()
|
||||
.replace("? extends ", "")
|
||||
.replace("class ", ""));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
private void printTree(Class<?> parent, int indent) {
|
||||
for (Class<?> clazz : hierarchy.get(parent)) {
|
||||
System.out.println(new StringBuilder(Strings.repeat(" ", indent))
|
||||
.append(indent == 0 ? "" : "↳ ")
|
||||
.append(getPrintableName(clazz))
|
||||
.append(isAbstract(clazz.getModifiers()) ? " (abstract)" : "")
|
||||
.append(clazz.isAnnotationPresent(VirtualEntity.class) ? " (virtual)" : "")
|
||||
.append(clazz.isAnnotationPresent(NotBackedUp.class) ? " (not backed up)" : "")
|
||||
.append(BackupGroupRoot.class.isAssignableFrom(clazz) ? " (bgr)" : ""));
|
||||
printSubclasses(clazz, indent + 2);
|
||||
printTree(clazz, indent + 2);
|
||||
if (indent == 0) {
|
||||
System.out.println(); // Separate the entity groups with a line.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printSubclasses(Class<?> parent, int indent) {
|
||||
for (Class<?> clazz : superclassToSubclasses.get(parent)) {
|
||||
System.out.println(new StringBuilder(Strings.repeat(" ", indent))
|
||||
.append("- ")
|
||||
.append(getPrintableName(clazz))
|
||||
.append(clazz.isAnnotationPresent(EntitySubclass.class) ? " (subclass)" : ""));
|
||||
printSubclasses(clazz, indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
static String getPrintableName(Class<?> clazz) {
|
||||
return clazz.isMemberClass()
|
||||
? getPrintableName(clazz.getDeclaringClass()) + "." + clazz.getSimpleName()
|
||||
: clazz.getSimpleName();
|
||||
}
|
||||
|
||||
static class PrintableNameOrdering extends Ordering<Class<?>> implements Serializable {
|
||||
@Override
|
||||
public int compare(Class<?> left, Class<?> right) {
|
||||
return getPrintableName(left).compareTo(getPrintableName(right));
|
||||
}
|
||||
}
|
||||
}
|
43
java/google/registry/tools/GetTldCommand.java
Normal file
43
java/google/registry/tools/GetTldCommand.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to show a TLD record. */
|
||||
@Parameters(separators = " =", commandDescription = "Show TLD record(s)")
|
||||
final class GetTldCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "TLD(s) to show",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (String tld : mainParameters) {
|
||||
System.out.println(Registry.get(assertTldExists(tld)));
|
||||
}
|
||||
}
|
||||
}
|
124
java/google/registry/tools/GhostrydeCommand.java
Normal file
124
java/google/registry/tools/GhostrydeCommand.java
Normal file
|
@ -0,0 +1,124 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.domain.registry.keyring.api.KeyModule.Key;
|
||||
import com.google.domain.registry.rde.Ghostryde;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to encrypt/decrypt {@code .ghostryde} files. */
|
||||
@Parameters(separators = " =", commandDescription = "Encrypt/decrypt a ghostryde file.")
|
||||
final class GhostrydeCommand implements Command {
|
||||
|
||||
@Parameter(
|
||||
names = {"-e", "--encrypt"},
|
||||
description = "Encrypt mode.")
|
||||
private boolean encrypt;
|
||||
|
||||
@Parameter(
|
||||
names = {"-d", "--decrypt"},
|
||||
description = "Decrypt mode.")
|
||||
private boolean decrypt;
|
||||
|
||||
@Parameter(
|
||||
names = {"-i", "--input"},
|
||||
description = "Input file.",
|
||||
validateWith = PathParameter.InputFile.class)
|
||||
private Path input = Paths.get("/dev/stdin");
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file. If this is a directory, then in --encrypt mode, the output "
|
||||
+ "filename will be the input filename with '.ghostryde' appended, and in --decrypt "
|
||||
+ "mode, the output filename will be determined based on the name stored within the "
|
||||
+ "archive.",
|
||||
validateWith = PathParameter.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Inject
|
||||
Ghostryde ghostryde;
|
||||
|
||||
@Inject
|
||||
@Key("rdeStagingEncryptionKey")
|
||||
PGPPublicKey rdeStagingEncryptionKey;
|
||||
|
||||
@Inject
|
||||
@Key("rdeStagingDecryptionKey")
|
||||
PGPPrivateKey rdeStagingDecryptionKey;
|
||||
|
||||
@Override
|
||||
public final void run() throws Exception {
|
||||
checkArgument(encrypt ^ decrypt, "Please specify either --encrypt or --decrypt");
|
||||
if (encrypt) {
|
||||
runEncrypt();
|
||||
} else {
|
||||
runDecrypt();
|
||||
}
|
||||
}
|
||||
|
||||
private void runEncrypt() throws IOException, PGPException {
|
||||
Path outFile = Files.isDirectory(output)
|
||||
? output.resolve(input.getFileName() + ".ghostryde")
|
||||
: output;
|
||||
try (OutputStream out = Files.newOutputStream(outFile);
|
||||
Ghostryde.Encryptor encryptor =
|
||||
ghostryde.openEncryptor(out, rdeStagingEncryptionKey);
|
||||
Ghostryde.Compressor kompressor = ghostryde.openCompressor(encryptor);
|
||||
Ghostryde.Output ghostOutput =
|
||||
ghostryde.openOutput(kompressor, input.getFileName().toString(),
|
||||
new DateTime(Files.getLastModifiedTime(input).toMillis(), UTC));
|
||||
InputStream in = Files.newInputStream(input)) {
|
||||
ByteStreams.copy(in, ghostOutput);
|
||||
}
|
||||
}
|
||||
|
||||
private void runDecrypt() throws IOException, PGPException {
|
||||
try (InputStream in = Files.newInputStream(input);
|
||||
Ghostryde.Decryptor decryptor =
|
||||
ghostryde.openDecryptor(in, rdeStagingDecryptionKey);
|
||||
Ghostryde.Decompressor decompressor = ghostryde.openDecompressor(decryptor);
|
||||
Ghostryde.Input ghostInput = ghostryde.openInput(decompressor)) {
|
||||
Path outFile = Files.isDirectory(output)
|
||||
? output.resolve(ghostInput.getName())
|
||||
: output;
|
||||
Files.copy(ghostInput, outFile, REPLACE_EXISTING);
|
||||
Files.setLastModifiedTime(outFile,
|
||||
FileTime.fromMillis(ghostInput.getModified().getMillis()));
|
||||
}
|
||||
}
|
||||
}
|
81
java/google/registry/tools/GtechTool.java
Normal file
81
java/google/registry/tools/GtechTool.java
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
/** Command line interface with a subset of commands that are safe for tech support to run. */
|
||||
public final class GtechTool {
|
||||
|
||||
/**
|
||||
* Commands that exist in both {@link GtechTool} and {@link RegistryTool}.
|
||||
*
|
||||
* <p><b>Note:</b> If changing the command-line name of any commands below, remember to resolve
|
||||
* any invocations in scripts (e.g. PDT, ICANN reporting).
|
||||
*/
|
||||
static final ImmutableMap<String, Class<? extends GtechCommand>> COMMAND_MAP =
|
||||
ImmutableSortedMap.<String, Class<? extends GtechCommand>>naturalOrder()
|
||||
.put("auction_status", AuctionStatusCommand.class)
|
||||
.put("canonicalize_labels", CanonicalizeLabelsCommand.class)
|
||||
.put("convert_idn", ConvertIdnCommand.class)
|
||||
.put("create_anchor_tenant", CreateAnchorTenantCommand.class)
|
||||
.put("create_contact", CreateContactCommand.class)
|
||||
.put("create_credit", CreateCreditCommand.class)
|
||||
.put("create_credit_balance", CreateCreditBalanceCommand.class)
|
||||
.put("create_registrar_groups", CreateRegistrarGroupsCommand.class)
|
||||
.put("create_registrar", CreateRegistrarCommand.class)
|
||||
.put("create_sandbox_tld", CreateSandboxTldCommand.class)
|
||||
.put("delete_domain", DeleteDomainCommand.class)
|
||||
.put("domain_application_info", DomainApplicationInfoCommand.class)
|
||||
.put("domain_check", DomainCheckCommand.class)
|
||||
.put("domain_check_claims", DomainCheckClaimsCommand.class)
|
||||
.put("domain_check_fee", DomainCheckFeeCommand.class)
|
||||
.put("generate_applications_report", GenerateApplicationsReportCommand.class)
|
||||
.put("generate_auction_data", GenerateAuctionDataCommand.class)
|
||||
.put("generate_dns_report", GenerateDnsReportCommand.class)
|
||||
.put("get_application", GetApplicationCommand.class)
|
||||
.put("get_application_ids", GetApplicationIdsCommand.class)
|
||||
.put("get_applied_labels", GetAppliedLabelsCommand.class)
|
||||
.put("get_contact", GetContactCommand.class)
|
||||
.put("get_domain", GetDomainCommand.class)
|
||||
.put("get_history_entries", GetHistoryEntriesCommand.class)
|
||||
.put("get_host", GetHostCommand.class)
|
||||
.put("get_registrar", GetRegistrarCommand.class)
|
||||
.put("get_schema", GetSchemaCommand.class)
|
||||
.put("get_schema_tree", GetSchemaTreeCommand.class)
|
||||
.put("get_tld", GetTldCommand.class)
|
||||
.put("hash_certificate", HashCertificateCommand.class)
|
||||
.put("list_credits", ListCreditsCommand.class)
|
||||
.put("list_registrars", ListRegistrarsCommand.class)
|
||||
.put("list_tlds", ListTldsCommand.class)
|
||||
.put("publish_detail_report", PublishDetailReportCommand.class)
|
||||
.put("registrar_activity_report", RegistrarActivityReportCommand.class)
|
||||
.put("registrar_contact", RegistrarContactCommand.class)
|
||||
.put("setup_ote", SetupOteCommand.class)
|
||||
.put("update_registrar", UpdateRegistrarCommand.class)
|
||||
.put("update_sandbox_tld", UpdateSandboxTldCommand.class)
|
||||
.put("update_server_locks", UpdateServerLocksCommand.class)
|
||||
.put("validate_login_credentials", ValidateLoginCredentialsCommand.class)
|
||||
.put("verify_ote", VerifyOteCommand.class)
|
||||
.put("whois_query", WhoisQueryCommand.class)
|
||||
.build();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
RegistryToolEnvironment.parseFromArgs(args).setup();
|
||||
new RegistryCli().run("gtech_tool", args, COMMAND_MAP);
|
||||
}
|
||||
}
|
51
java/google/registry/tools/HashCertificateCommand.java
Normal file
51
java/google/registry/tools/HashCertificateCommand.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.util.X509Utils.getCertificateHash;
|
||||
import static com.google.domain.registry.util.X509Utils.loadCertificate;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to hash a client certificate. */
|
||||
@Parameters(commandDescription = "Hash a client certificate")
|
||||
final class HashCertificateCommand implements GtechCommand {
|
||||
|
||||
@Parameter(description = "Certificate filename")
|
||||
List<String> mainParameters = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void run() throws IOException, CertificateParsingException {
|
||||
checkArgument(mainParameters.size() <= 1,
|
||||
"Expected at most one argument with the certificate filename. Actual: %s",
|
||||
Joiner.on(' ').join(mainParameters));
|
||||
if (mainParameters.isEmpty()) {
|
||||
System.out.println(getCertificateHash(loadCertificate(System.in)));
|
||||
} else {
|
||||
System.out.println(getCertificateHash(loadCertificate(Paths.get(mainParameters.get(0)))));
|
||||
}
|
||||
}
|
||||
}
|
48
java/google/registry/tools/HelpCommand.java
Normal file
48
java/google/registry/tools/HelpCommand.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** A command to display per-command usage. */
|
||||
@Parameters(commandDescription = "Get usage for a command")
|
||||
final class HelpCommand implements Command {
|
||||
|
||||
private final JCommander jcommander;
|
||||
|
||||
HelpCommand(JCommander jcommander) {
|
||||
this.jcommander = jcommander;
|
||||
}
|
||||
|
||||
@Parameter(description = "<command>")
|
||||
private List<String> mainParameters = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String target = getOnlyElement(mainParameters, null);
|
||||
if (target == null) {
|
||||
jcommander.usage();
|
||||
} else {
|
||||
jcommander.usage(target);
|
||||
}
|
||||
}
|
||||
}
|
56
java/google/registry/tools/Injector.java
Normal file
56
java/google/registry/tools/Injector.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
// 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;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Utilities for dependency injection using Dagger2.
|
||||
*/
|
||||
final class Injector {
|
||||
|
||||
/**
|
||||
* Reflectively injects the dependencies of an object instance using Dagger2.
|
||||
*
|
||||
* <p>There must exist a method named {@code inject} on your {@code component} accepting a single
|
||||
* parameter whose type is identical to the class of {@code object}.
|
||||
*
|
||||
* <p><b>Note:</b> This is obviously slow since it uses reflection, which goes against the entire
|
||||
* philosophy of Dagger2. This method is only useful if you want to avoid avoid the boilerplate of
|
||||
* a super long chain of instanceof if statements, and you aren't concerned about performance.
|
||||
*
|
||||
* @return {@code true} if an appropriate injection method existed
|
||||
*/
|
||||
static <T> boolean injectReflectively(Class<T> componentType, T component, Object object) {
|
||||
for (Method method : componentType.getMethods()) {
|
||||
if (!method.getName().equals("inject")) {
|
||||
continue;
|
||||
}
|
||||
Class<?> type = method.getParameterTypes()[0];
|
||||
if (type == object.getClass()) {
|
||||
try {
|
||||
method.invoke(component, object);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Injector() {}
|
||||
}
|
76
java/google/registry/tools/ListCreditsCommand.java
Normal file
76
java/google/registry/tools/ListCreditsCommand.java
Normal file
|
@ -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;
|
||||
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit;
|
||||
import com.google.domain.registry.model.billing.RegistrarCreditBalance.BalanceMap;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Work;
|
||||
|
||||
import org.joda.money.Money;
|
||||
|
||||
/** Command to list registrar credits and balances. */
|
||||
@Parameters(commandDescription = "List registrar credits and balances")
|
||||
final class ListCreditsCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-a", "--all"},
|
||||
description = "Pass this flag to show all credits, even those with a zero active balance")
|
||||
private boolean showAll;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
ImmutableList<String> creditStrings = createCreditStrings(registrar);
|
||||
if (!creditStrings.isEmpty()) {
|
||||
System.out.println(registrar.getClientIdentifier());
|
||||
System.out.print(Joiner.on("").join(creditStrings));
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ImmutableList<String> createCreditStrings(final Registrar registrar) {
|
||||
return ofy()
|
||||
.transactNewReadOnly(
|
||||
new Work<ImmutableList<String>>() {
|
||||
@Override
|
||||
public ImmutableList<String> run() {
|
||||
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
|
||||
for (RegistrarCredit credit : RegistrarCredit.loadAllForRegistrar(registrar)) {
|
||||
BalanceMap balanceMap = BalanceMap.createForCredit(credit);
|
||||
Optional<Money> activeBalance =
|
||||
balanceMap.getActiveBalanceAtTime(ofy().getTransactionTime());
|
||||
// Unless showAll is true, only show credits with a positive active balance (which
|
||||
// excludes just zero-balance credits since credit balances cannot be negative).
|
||||
if (showAll || (activeBalance.isPresent() && activeBalance.get().isPositive())) {
|
||||
builder.add(credit.getSummary() + "\n" + balanceMap);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
72
java/google/registry/tools/ListCursorsCommand.java
Normal file
72
java/google/registry/tools/ListCursorsCommand.java
Normal file
|
@ -0,0 +1,72 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.domain.registry.model.registry.Registries;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.Registry.TldType;
|
||||
import com.google.domain.registry.model.registry.RegistryCursor;
|
||||
import com.google.domain.registry.model.registry.RegistryCursor.CursorType;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Lists {@link RegistryCursor} timestamps used by locking rolling cursor tasks, like in RDE. */
|
||||
@Parameters(separators = " =", commandDescription = "Lists cursor timestamps used by LRC tasks")
|
||||
final class ListCursorsCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--type",
|
||||
description = "Which cursor to list.",
|
||||
required = true)
|
||||
private CursorType cursorType;
|
||||
|
||||
@Parameter(
|
||||
names = "--tld_type",
|
||||
description = "Filter TLDs of a certain type (REAL or TEST.)")
|
||||
private TldType filterTldType = TldType.REAL;
|
||||
|
||||
@Parameter(
|
||||
names = "--escrow_enabled",
|
||||
description = "Filter TLDs to only include those with RDE escrow enabled.")
|
||||
private boolean filterEscrowEnabled;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
List<String> lines = new ArrayList<>();
|
||||
for (String tld : Registries.getTlds()) {
|
||||
Registry registry = Registry.get(tld);
|
||||
if (filterTldType != registry.getTldType()) {
|
||||
continue;
|
||||
}
|
||||
if (filterEscrowEnabled && !registry.getEscrowEnabled()) {
|
||||
continue;
|
||||
}
|
||||
Optional<DateTime> cursor = RegistryCursor.load(registry, cursorType);
|
||||
lines.add(String.format("%-25s%s", cursor.isPresent() ? cursor.get() : "absent", tld));
|
||||
}
|
||||
for (String line : Ordering.natural().sortedCopy(lines)) {
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
}
|
45
java/google/registry/tools/ListDomainsCommand.java
Normal file
45
java/google/registry/tools/ListDomainsCommand.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.tools.server.ListDomainsAction;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command to list all second-level domains associated with a TLD. */
|
||||
|
||||
@Parameters(separators = " =", commandDescription = "List domains associated with a TLD.")
|
||||
final class ListDomainsCommand extends ListObjectsCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "Top level domain whose second-level domains shall be listed.",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Override
|
||||
String getCommandPath() {
|
||||
return ListDomainsAction.PATH;
|
||||
}
|
||||
|
||||
/** Returns a map of parameters to be sent to the server
|
||||
* (in addition to the usual ones). */
|
||||
@Override
|
||||
ImmutableMap<String, Object> getParameterMap() {
|
||||
return ImmutableMap.<String, Object>of("tld", tld);
|
||||
}
|
||||
}
|
29
java/google/registry/tools/ListHostsCommand.java
Normal file
29
java/google/registry/tools/ListHostsCommand.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.tools.server.ListHostsAction;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command to list all HostResource entities associated with a TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "List hosts associated with a TLD.")
|
||||
final class ListHostsCommand extends ListObjectsCommand {
|
||||
|
||||
@Override
|
||||
String getCommandPath() {
|
||||
return ListHostsAction.PATH;
|
||||
}
|
||||
}
|
141
java/google/registry/tools/ListObjectsCommand.java
Normal file
141
java/google/registry/tools/ListObjectsCommand.java
Normal file
|
@ -0,0 +1,141 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
||||
import static com.google.domain.registry.tools.server.ListObjectsAction.FIELDS_PARAM;
|
||||
import static com.google.domain.registry.tools.server.ListObjectsAction.FULL_FIELD_NAMES_PARAM;
|
||||
import static com.google.domain.registry.tools.server.ListObjectsAction.PRINT_HEADER_ROW_PARAM;
|
||||
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract base class for commands that list objects by calling a server task.
|
||||
*
|
||||
* <p>The formatting is done on the server side; this class just dumps the results to the screen.
|
||||
*/
|
||||
abstract class ListObjectsCommand implements RemoteApiCommand, ServerSideCommand {
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-f", "--fields"},
|
||||
description = "Comma-separated list of fields to show for each object listed")
|
||||
private String fields;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"--header"},
|
||||
description = "Whether or not to print a header row for the resulting output - default is to "
|
||||
+ "only print headers when more than one column is output",
|
||||
arity = 1)
|
||||
private Boolean printHeaderRow;
|
||||
|
||||
@Parameter(
|
||||
names = {"--full_field_names"},
|
||||
description = "Whether to print full field names in header row (as opposed to aliases)")
|
||||
private boolean fullFieldNames = false;
|
||||
|
||||
private Connection connection;
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/** Returns the path to the servlet task. */
|
||||
abstract String getCommandPath();
|
||||
|
||||
/** Returns a map of parameters to be sent to the server
|
||||
* (in addition to the usual ones). */
|
||||
@Nullable
|
||||
ImmutableMap<String, Object> getParameterMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
ImmutableMap.Builder<String, Object> params = ImmutableMap.<String, Object>builder();
|
||||
if (fields != null) {
|
||||
params.put(FIELDS_PARAM, fields);
|
||||
}
|
||||
if (printHeaderRow != null) {
|
||||
params.put(PRINT_HEADER_ROW_PARAM, printHeaderRow);
|
||||
}
|
||||
if (fullFieldNames) {
|
||||
params.put(FULL_FIELD_NAMES_PARAM, Boolean.TRUE);
|
||||
}
|
||||
ImmutableMap<String, Object> extraParams = getParameterMap();
|
||||
if (extraParams != null) {
|
||||
params.putAll(extraParams);
|
||||
}
|
||||
// Call the server and get the response data.
|
||||
String response = connection.send(
|
||||
getCommandPath(),
|
||||
params.build(),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
// Parse the returned JSON and make sure it's a map.
|
||||
Object obj = JSONValue.parse(response.substring(JSON_SAFETY_PREFIX.length()));
|
||||
if (!(obj instanceof Map<?, ?>)) {
|
||||
throw new VerifyException("Server returned unexpected JSON: " + response);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> responseMap = (Map<String, Object>) obj;
|
||||
// Get the status.
|
||||
obj = responseMap.get("status");
|
||||
if (obj == null) {
|
||||
throw new VerifyException("Server returned no status");
|
||||
}
|
||||
if (!(obj instanceof String)) {
|
||||
throw new VerifyException("Server returned non-string status");
|
||||
}
|
||||
String status = (String) obj;
|
||||
// Handle errors.
|
||||
if (status.equals("error")) {
|
||||
obj = responseMap.get("error");
|
||||
if (obj == null) {
|
||||
throw new VerifyException("Server returned no error message");
|
||||
}
|
||||
System.out.println(obj);
|
||||
// Handle success.
|
||||
} else if (status.equals("success")) {
|
||||
obj = responseMap.get("lines");
|
||||
if (obj == null) {
|
||||
throw new VerifyException("Server returned no response data");
|
||||
}
|
||||
if (!(obj instanceof List<?>)) {
|
||||
throw new VerifyException("Server returned unexpected response data");
|
||||
}
|
||||
for (Object lineObj : (List<?>) obj) {
|
||||
System.out.println(lineObj);
|
||||
}
|
||||
// Handle unexpected status values.
|
||||
} else {
|
||||
throw new VerifyException("Server returned unexpected status");
|
||||
}
|
||||
}
|
||||
}
|
29
java/google/registry/tools/ListPremiumListsCommand.java
Normal file
29
java/google/registry/tools/ListPremiumListsCommand.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.tools.server.ListPremiumListsAction;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command to list all premium lists. */
|
||||
@Parameters(separators = " =", commandDescription = "List all premium lists.")
|
||||
final class ListPremiumListsCommand extends ListObjectsCommand {
|
||||
|
||||
@Override
|
||||
String getCommandPath() {
|
||||
return ListPremiumListsAction.PATH;
|
||||
}
|
||||
}
|
30
java/google/registry/tools/ListRegistrarsCommand.java
Normal file
30
java/google/registry/tools/ListRegistrarsCommand.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.server.ListRegistrarsAction;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command to list all registrars. */
|
||||
@Parameters(separators = " =", commandDescription = "List all registrars.")
|
||||
final class ListRegistrarsCommand extends ListObjectsCommand implements GtechCommand {
|
||||
|
||||
@Override
|
||||
String getCommandPath() {
|
||||
return ListRegistrarsAction.PATH;
|
||||
}
|
||||
}
|
29
java/google/registry/tools/ListReservedListsCommand.java
Normal file
29
java/google/registry/tools/ListReservedListsCommand.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.tools.server.ListReservedListsAction;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command to list all reserved lists. */
|
||||
@Parameters(separators = " =", commandDescription = "List all reserved lists.")
|
||||
final class ListReservedListsCommand extends ListObjectsCommand {
|
||||
|
||||
@Override
|
||||
String getCommandPath() {
|
||||
return ListReservedListsAction.PATH;
|
||||
}
|
||||
}
|
30
java/google/registry/tools/ListTldsCommand.java
Normal file
30
java/google/registry/tools/ListTldsCommand.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.server.ListTldsAction;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
/** Command to list all top-level domains. */
|
||||
@Parameters(separators = " =", commandDescription = "List all top-level domains.")
|
||||
final class ListTldsCommand extends ListObjectsCommand implements GtechCommand {
|
||||
|
||||
@Override
|
||||
String getCommandPath() {
|
||||
return ListTldsAction.PATH;
|
||||
}
|
||||
}
|
126
java/google/registry/tools/LoadSnapshotCommand.java
Normal file
126
java/google/registry/tools/LoadSnapshotCommand.java
Normal file
|
@ -0,0 +1,126 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Predicates.notNull;
|
||||
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.domain.registry.bigquery.BigqueryUtils.SourceFormat;
|
||||
import com.google.domain.registry.export.ExportConstants;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Command to load datastore snapshots into Bigquery. */
|
||||
@Parameters(separators = " =", commandDescription = "Load datastore snapshot into Bigquery")
|
||||
final class LoadSnapshotCommand extends BigqueryCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--snapshot",
|
||||
description = "Common filename prefix of the specific snapshot series to import.")
|
||||
private String snapshotPrefix = null;
|
||||
|
||||
@Parameter(
|
||||
names = "--gcs_bucket",
|
||||
description = "Name of the GCS bucket from which to import datastore snapshots.")
|
||||
private String snapshotGcsBucket = "domain-registry/snapshots/testing";
|
||||
|
||||
@Parameter(
|
||||
names = "--kinds",
|
||||
description = "List of datastore kinds for which to import snapshot data.")
|
||||
private List<String> kindNames = new ArrayList<>(ExportConstants.getReportingKinds());
|
||||
|
||||
/** Runs the main snapshot import logic. */
|
||||
@Override
|
||||
public void runWithBigquery() throws Exception {
|
||||
kindNames.removeAll(ImmutableList.of("")); // Filter out any empty kind names.
|
||||
if (snapshotPrefix == null || kindNames.isEmpty()) {
|
||||
System.err.println("Nothing to import; specify --snapshot and at least one kind.");
|
||||
return;
|
||||
}
|
||||
Map<String, ListenableFuture<?>> loadJobs = loadSnapshotKinds(kindNames);
|
||||
waitForLoadJobs(loadJobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts load jobs for the given snapshot kinds, and returns a map of kind name to
|
||||
* ListenableFuture representing the result of the load job for that kind.
|
||||
*/
|
||||
private Map<String, ListenableFuture<?>> loadSnapshotKinds(List<String> kindNames)
|
||||
throws Exception {
|
||||
ImmutableMap.Builder<String, ListenableFuture<?>> builder = new ImmutableMap.Builder<>();
|
||||
for (String kind : kindNames) {
|
||||
String filename = String.format(
|
||||
"gs://%s/%s.%s.backup_info", snapshotGcsBucket, snapshotPrefix, kind);
|
||||
builder.put(kind, loadSnapshotFile(filename, kind));
|
||||
System.err.println("Started load job for kind: " + kind);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/** Starts a load job for the specified kind name, sourcing data from the given GCS file. */
|
||||
private ListenableFuture<?> loadSnapshotFile(String filename, String kindName) throws Exception {
|
||||
return bigquery().load(
|
||||
bigquery().buildDestinationTable(kindName)
|
||||
.description("Datastore snapshot import for " + kindName + ".")
|
||||
.build(),
|
||||
SourceFormat.DATASTORE_BACKUP,
|
||||
ImmutableList.of(filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Block on the completion of the load jobs in the provided map, printing out information on
|
||||
* each job's success or failure.
|
||||
*/
|
||||
private void waitForLoadJobs(Map<String, ListenableFuture<?>> loadJobs) throws Exception {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
System.err.println("Waiting for load jobs...");
|
||||
// Add callbacks to each load job that print information on successful completion or failure.
|
||||
for (final String jobId : loadJobs.keySet()) {
|
||||
final String jobName = "load-" + jobId;
|
||||
Futures.addCallback(loadJobs.get(jobId), new FutureCallback<Object>() {
|
||||
private double elapsedSeconds() {
|
||||
return (System.currentTimeMillis() - startTime) / 1000.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Object unused) {
|
||||
System.err.printf("Job %s succeeded (%.3fs)\n", jobName, elapsedSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
System.err.printf(
|
||||
"Job %s failed (%.3fs): %s\n", jobName, elapsedSeconds(), error.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
// Block on the completion of all the load jobs.
|
||||
List<?> results = Futures.successfulAsList(loadJobs.values()).get();
|
||||
int numSucceeded = FluentIterable.from(results).filter(notNull()).size();
|
||||
System.err.printf(
|
||||
"All load jobs have terminated: %d/%d successful.\n",
|
||||
numSucceeded, loadJobs.size());
|
||||
}
|
||||
}
|
78
java/google/registry/tools/LoggingParameters.java
Normal file
78
java/google/registry/tools/LoggingParameters.java
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.domain.registry.util.ResourceUtils.readResourceBytes;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Parameter delegate class to handle logging configuration for {@link RegistryCli}. */
|
||||
@Parameters(separators = " =")
|
||||
final class LoggingParameters {
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--log_level",
|
||||
description = "Default level at which to log messages")
|
||||
private Level logLevel;
|
||||
|
||||
@Parameter(
|
||||
names = "--logging_configs",
|
||||
description = "Comma-delimited list of logging properties to add to the logging.properties "
|
||||
+ "file, e.g. com.example.level=WARNING,com.example.FooClass.level=SEVERE")
|
||||
private List<String> configLines = new ArrayList<>();
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--logging_properties_file",
|
||||
description = "File from which to read custom logging properties",
|
||||
validateWith = PathParameter.InputFile.class)
|
||||
private Path configFile;
|
||||
|
||||
private static final ByteSource DEFAULT_LOG_CONFIG =
|
||||
readResourceBytes(LoggingParameters.class, "logging.properties");
|
||||
|
||||
void configureLogging() throws IOException {
|
||||
ByteSource baseConfig = (configFile != null)
|
||||
? Files.asByteSource(configFile.toFile())
|
||||
: DEFAULT_LOG_CONFIG;
|
||||
if (logLevel != null) {
|
||||
configLines.add(".level = " + logLevel);
|
||||
}
|
||||
// Add an extra leading newline in case base properties file does not end in a newline.
|
||||
String customProperties = "\n" + Joiner.on('\n').join(configLines);
|
||||
ByteSource logConfig =
|
||||
ByteSource.concat(baseConfig, ByteSource.wrap(customProperties.getBytes()));
|
||||
try (InputStream input = logConfig.openStream()) {
|
||||
LogManager.getLogManager().readConfiguration(input);
|
||||
}
|
||||
}
|
||||
}
|
219
java/google/registry/tools/MakeBillingTablesCommand.java
Normal file
219
java/google/registry/tools/MakeBillingTablesCommand.java
Normal file
|
@ -0,0 +1,219 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.tools.BigqueryCommandUtilities.handleTableCreation;
|
||||
import static com.google.domain.registry.util.ResourceUtils.readResourceUtf8;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.domain.registry.bigquery.BigqueryUtils.TableType;
|
||||
import com.google.domain.registry.tools.BigqueryCommandUtilities.TableCreationException;
|
||||
import com.google.domain.registry.util.SqlTemplate;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to make synthetic billing tables and views in Bigquery. */
|
||||
@Parameters(separators = " =", commandDescription = "Make synthetic billing tables in Bigquery")
|
||||
final class MakeBillingTablesCommand extends BigqueryCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--source_dataset",
|
||||
description = "Name of the dataset containing the source entity tables.")
|
||||
private String sourceDatasetId;
|
||||
|
||||
// TODO(b/14297938): we should be picking up the TLDs to bill automatically based on the tldType
|
||||
// and tldState rather than having to manually pass them in.
|
||||
@Parameter(
|
||||
names = "--tlds",
|
||||
description = "TLD(s) to include in billing data table",
|
||||
required = true)
|
||||
private List<String> tlds;
|
||||
|
||||
private static final SqlTemplate CURRENCY_TABLE_SQL = getSql("currency_table.sql");
|
||||
private static final SqlTemplate REGISTRAR_DATA_SQL = getSql("registrar_data_view.sql");
|
||||
private static final SqlTemplate REGISTRY_DATA_SQL = getSql("registry_data_view.sql");
|
||||
private static final SqlTemplate CREDIT_DATA_SQL = getSql("credit_data_view.sql");
|
||||
private static final SqlTemplate CREDIT_BALANCE_DATA_SQL = getSql("credit_balance_data_view.sql");
|
||||
private static final SqlTemplate PREMIUM_LIST_DATA_SQL = getSql("premium_list_data_view.sql");
|
||||
private static final SqlTemplate RECURRING_DATA_SQL = getSql("recurring_event_data_view.sql");
|
||||
private static final SqlTemplate BILLING_DATA_SQL = getSql("billing_data_view.sql");
|
||||
|
||||
/** Runs the main billing table/view creation logic. */
|
||||
@Override
|
||||
public void runWithBigquery() throws Exception {
|
||||
// Make the source dataset default to the default destination dataset if it has not been set.
|
||||
sourceDatasetId = Optional.fromNullable(sourceDatasetId).or(bigquery().getDatasetId());
|
||||
checkArgument(!tlds.isEmpty(), "Must specify at least 1 TLD to include in billing data table");
|
||||
// TODO(b/19016191): Should check that input tables exist up front, and avoid later errors.
|
||||
try {
|
||||
makeCurrencyTable();
|
||||
makeRegistrarView();
|
||||
makeRegistryView();
|
||||
makeCreditView();
|
||||
makeCreditBalanceView();
|
||||
makePremiumListView();
|
||||
makeRecurringEventView();
|
||||
makeBillingView();
|
||||
} catch (TableCreationException e) {
|
||||
// Swallow since we already will have printed an error message.
|
||||
}
|
||||
}
|
||||
|
||||
/** Generates a table of currency information. */
|
||||
// TODO(b/19016720): once there is a registry-wide currency, this method should compute the set of
|
||||
// currencies needed for the active registries, look up the exponent for each currency from Joda
|
||||
// CurrencyUnit data, and then auto-generate the query to construct this table.
|
||||
// NB: "exponent" is the ISO 4217 name for the number of decimal places used by a currency.
|
||||
private void makeCurrencyTable() throws Exception {
|
||||
handleTableCreation(
|
||||
"currency table",
|
||||
bigquery().query(
|
||||
CURRENCY_TABLE_SQL
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("Currency")
|
||||
.description("Generated table of currency information.")
|
||||
.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a view of registrar data, used as an intermediate so that the invoice generation
|
||||
* stage doesn't have to refer all the way back to the original managed backup dataset.
|
||||
*/
|
||||
private void makeRegistrarView() throws Exception {
|
||||
handleTableCreation(
|
||||
"registrar data view",
|
||||
bigquery().query(
|
||||
REGISTRAR_DATA_SQL
|
||||
.put("SOURCE_DATASET", sourceDatasetId)
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("RegistrarData")
|
||||
.description("Synthetic view of registrar information.")
|
||||
.type(TableType.VIEW)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/** Generates a view of registry data to feed into later views. */
|
||||
private void makeRegistryView() throws Exception {
|
||||
handleTableCreation(
|
||||
"registry data view",
|
||||
bigquery().query(
|
||||
REGISTRY_DATA_SQL
|
||||
.put("SOURCE_DATASET", sourceDatasetId)
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("RegistryData")
|
||||
.description("Synthetic view of registry information.")
|
||||
.type(TableType.VIEW)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a view of registrar credit entities that links in information from the owning
|
||||
* Registrar (e.g. billing ID).
|
||||
*/
|
||||
private void makeCreditView() throws Exception {
|
||||
handleTableCreation(
|
||||
"credit data view",
|
||||
bigquery().query(
|
||||
CREDIT_DATA_SQL
|
||||
.put("SOURCE_DATASET", sourceDatasetId)
|
||||
.put("DEST_DATASET", bigquery().getDatasetId())
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("CreditData")
|
||||
.description("Synthetic view of registrar credit information.")
|
||||
.type(TableType.VIEW)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a view of registrar credit balance entities that collapses them down to the one
|
||||
* 'true' credit balance for a given credit ID and effective time, eliminating any duplicates by
|
||||
* choosing the most recently written balance entry of the set.
|
||||
* <p>
|
||||
* The result is a list of the historical balances of each credit (according to the most recent
|
||||
* data written) that can be used to find the active balance of a credit at any point in time.
|
||||
*/
|
||||
private void makeCreditBalanceView() throws Exception {
|
||||
handleTableCreation(
|
||||
"credit balance data view",
|
||||
bigquery().query(
|
||||
CREDIT_BALANCE_DATA_SQL
|
||||
.put("SOURCE_DATASET", sourceDatasetId)
|
||||
.put("DEST_DATASET", bigquery().getDatasetId())
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("CreditBalanceData")
|
||||
.description("Synthetic view of registrar credit balance information.")
|
||||
.type(TableType.VIEW)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/** Generates a view of premium list data for each TLD. */
|
||||
private void makePremiumListView() throws Exception {
|
||||
handleTableCreation(
|
||||
"premium list data view",
|
||||
bigquery().query(
|
||||
PREMIUM_LIST_DATA_SQL
|
||||
.put("SOURCE_DATASET", sourceDatasetId)
|
||||
.put("DEST_DATASET", bigquery().getDatasetId())
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("PremiumListData")
|
||||
.description("Synthetic view of premium list data.")
|
||||
.type(TableType.VIEW)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/** Generates a view of recurring billing events expanded into individual recurrences. */
|
||||
private void makeRecurringEventView() throws Exception {
|
||||
handleTableCreation(
|
||||
"recurring event data view",
|
||||
bigquery().query(
|
||||
RECURRING_DATA_SQL
|
||||
.put("SOURCE_DATASET", sourceDatasetId)
|
||||
.put("DEST_DATASET", bigquery().getDatasetId())
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("RecurringEventData")
|
||||
.description("Synthetic view of recurring billing event recurrence data.")
|
||||
.type(TableType.VIEW)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a view of consolidated billing information that includes currency conversions,
|
||||
* registrar details, and cancellation flags on top of the original BillingEvent.OneTime data.
|
||||
*/
|
||||
private void makeBillingView() throws Exception {
|
||||
handleTableCreation(
|
||||
"billing data view",
|
||||
bigquery().query(
|
||||
BILLING_DATA_SQL
|
||||
.put("SOURCE_DATASET", sourceDatasetId)
|
||||
.put("DEST_DATASET", bigquery().getDatasetId())
|
||||
.put("TLDS", Joiner.on(",").join(tlds))
|
||||
.build(),
|
||||
bigquery().buildDestinationTable("BillingData")
|
||||
.description("Synthetic view of consolidated billing information.")
|
||||
.type(TableType.VIEW)
|
||||
.build()));
|
||||
}
|
||||
|
||||
private static SqlTemplate getSql(String filename) {
|
||||
return SqlTemplate.create(
|
||||
readResourceUtf8(MakeBillingTablesCommand.class, "sql/" + filename));
|
||||
}
|
||||
}
|
237
java/google/registry/tools/MutatingCommand.java
Normal file
237
java/google/registry/tools/MutatingCommand.java
Normal file
|
@ -0,0 +1,237 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
|
||||
import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
|
||||
import static com.google.common.base.Functions.toStringFunction;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.DatastoreServiceUtils.getNameOrId;
|
||||
import static com.google.domain.registry.util.DiffUtils.prettyPrintDeepDiff;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Optional;
|
||||
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.model.ImmutableObject;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** A {@link ConfirmingCommand} that changes objects in the datastore. */
|
||||
public abstract class MutatingCommand extends ConfirmingCommand implements RemoteApiCommand {
|
||||
|
||||
/**
|
||||
* A mutation of a specific entity, represented by an old and a new version of the entity.
|
||||
* Storing the old version is necessary to enable checking that the existing entity has not been
|
||||
* modified when applying a mutation that was created outside the same transaction.
|
||||
*/
|
||||
private static class EntityChange {
|
||||
|
||||
/** The possible types of mutation that can be performed on an entity. */
|
||||
public static enum ChangeType {
|
||||
CREATE, DELETE, UPDATE;
|
||||
|
||||
/** Return the ChangeType corresponding to the given combination of version existences. */
|
||||
public static ChangeType get(boolean hasOldVersion, boolean hasNewVersion) {
|
||||
checkArgument(
|
||||
hasOldVersion || hasNewVersion,
|
||||
"An entity change must have an old version or a new version (or both)");
|
||||
return !hasOldVersion ? CREATE : (!hasNewVersion ? DELETE : UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
/** The type of mutation being performed on the entity. */
|
||||
final ChangeType type;
|
||||
|
||||
/** The old version of the entity, or null if this is a create. */
|
||||
final ImmutableObject oldEntity;
|
||||
|
||||
/** The new version of the entity, or null if this is a delete. */
|
||||
final ImmutableObject newEntity;
|
||||
|
||||
/** The key that points to the entity being changed. */
|
||||
final Key<ImmutableObject> key;
|
||||
|
||||
public EntityChange(ImmutableObject oldEntity, ImmutableObject newEntity) {
|
||||
type = ChangeType.get(oldEntity != null, newEntity != null);
|
||||
checkArgument(
|
||||
type != ChangeType.UPDATE || Key.create(oldEntity).equals(Key.create(newEntity)),
|
||||
"Both entity versions in an update must have the same Key.");
|
||||
this.oldEntity = oldEntity;
|
||||
this.newEntity = newEntity;
|
||||
key = Key.create(MoreObjects.firstNonNull(oldEntity, newEntity));
|
||||
}
|
||||
|
||||
/** Returns a human-readable ID string for the entity being changed. */
|
||||
public String getEntityId() {
|
||||
return String.format(
|
||||
"%s@%s",
|
||||
key.getKind(),
|
||||
// NB: try name before id, since name defaults to null, whereas id defaults to 0.
|
||||
getNameOrId(key.getRaw()));
|
||||
}
|
||||
|
||||
/** Returns a string representation of this entity change. */
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"%s %s\n%s",
|
||||
UPPER_UNDERSCORE.to(UPPER_CAMEL, type.toString()),
|
||||
getEntityId(),
|
||||
type == ChangeType.UPDATE
|
||||
? Optional
|
||||
.fromNullable(emptyToNull(prettyPrintDeepDiff(
|
||||
oldEntity.toDiffableFieldMap(), newEntity.toDiffableFieldMap())))
|
||||
.or("[no changes]\n")
|
||||
: (MoreObjects.firstNonNull(oldEntity, newEntity) + "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Map from entity keys to EntityChange objects representing changes to those entities. */
|
||||
private final LinkedHashMap<Key<ImmutableObject>, EntityChange> changedEntitiesMap =
|
||||
new LinkedHashMap<>();
|
||||
|
||||
/** A set of resource keys for which new transactions should be created after. */
|
||||
private final Set<Key<ImmutableObject>> transactionBoundaries = new HashSet<>();
|
||||
|
||||
@Nullable
|
||||
private Key<ImmutableObject> lastAddedKey;
|
||||
|
||||
/**
|
||||
* Initializes the command.
|
||||
* <p>
|
||||
* Subclasses override this method to populate {@link #changedEntitiesMap} with updated
|
||||
* entities. The old entity is the key and the new entity is the value; the key is null for
|
||||
* newly created entities and the value is null for deleted entities.
|
||||
*/
|
||||
@Override
|
||||
protected abstract void init() throws Exception;
|
||||
|
||||
/**
|
||||
* Performs the command and returns a result description.
|
||||
*
|
||||
* <p>Subclasses can override this method if the command does something besides update entities,
|
||||
* such as running a full flow.
|
||||
*/
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
for (final List<EntityChange> batch : getCollatedEntityChangeBatches()) {
|
||||
ofy().transact(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
for (EntityChange change : batch) {
|
||||
// Load the key of the entity to mutate and double-check that it hasn't been
|
||||
// modified from the version that existed when the change was prepared.
|
||||
ImmutableObject existingEntity = ofy().load().key(change.key).now();
|
||||
checkState(
|
||||
Objects.equals(change.oldEntity, existingEntity),
|
||||
"Entity changed since init() was called.\n%s",
|
||||
prettyPrintDeepDiff(
|
||||
change.oldEntity == null ? ImmutableMap.of()
|
||||
: change.oldEntity.toDiffableFieldMap(),
|
||||
existingEntity == null ? ImmutableMap.of()
|
||||
: existingEntity.toDiffableFieldMap()));
|
||||
switch (change.type) {
|
||||
case CREATE: // Fall through.
|
||||
case UPDATE:
|
||||
ofy().save().entity(change.newEntity).now();
|
||||
break;
|
||||
case DELETE:
|
||||
ofy().delete().key(change.key).now();
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException(
|
||||
"Unknown entity change type: " + change.type);
|
||||
}
|
||||
}
|
||||
}});
|
||||
}
|
||||
return String.format("Updated %d entities.\n", changedEntitiesMap.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of lists of EntityChange actions to commit. Each list should be executed in
|
||||
* order inside a single transaction.
|
||||
*/
|
||||
private ImmutableSet<ImmutableList<EntityChange>> getCollatedEntityChangeBatches() {
|
||||
ImmutableSet.Builder<ImmutableList<EntityChange>> batches = new ImmutableSet.Builder<>();
|
||||
ArrayList<EntityChange> nextBatch = new ArrayList<>();
|
||||
for (EntityChange change : changedEntitiesMap.values()) {
|
||||
nextBatch.add(change);
|
||||
if (transactionBoundaries.contains(change.key)) {
|
||||
batches.add(ImmutableList.copyOf(nextBatch));
|
||||
nextBatch.clear();
|
||||
}
|
||||
}
|
||||
if (!nextBatch.isEmpty()) {
|
||||
batches.add(ImmutableList.copyOf(nextBatch));
|
||||
}
|
||||
return batches.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can call this to stage a mutation to an entity that will be applied by execute().
|
||||
* Note that both objects passed must correspond to versions of the same entity with the same key.
|
||||
*
|
||||
* @param oldEntity the existing version of the entity, or null to create a new entity
|
||||
* @param newEntity the new version of the entity to save, or null to delete the entity
|
||||
*/
|
||||
protected void stageEntityChange(
|
||||
@Nullable ImmutableObject oldEntity, @Nullable ImmutableObject newEntity) {
|
||||
EntityChange change = new EntityChange(oldEntity, newEntity);
|
||||
checkArgument(
|
||||
!changedEntitiesMap.containsKey(change.key),
|
||||
"Cannot apply multiple changes for the same entity: %s",
|
||||
change.getEntityId());
|
||||
changedEntitiesMap.put(change.key, change);
|
||||
lastAddedKey = change.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses can call this to write out all previously requested entity changes since the last
|
||||
* transaction flush in a transaction.
|
||||
*/
|
||||
protected void flushTransaction() {
|
||||
transactionBoundaries.add(checkNotNull(lastAddedKey));
|
||||
}
|
||||
|
||||
/** Returns the changes that have been staged thus far. */
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return changedEntitiesMap.isEmpty()
|
||||
? "No entity changes to apply."
|
||||
: Joiner.on("\n").join(FluentIterable
|
||||
.from(changedEntitiesMap.values())
|
||||
.transform(toStringFunction()));
|
||||
}
|
||||
}
|
41
java/google/registry/tools/MutatingEppToolCommand.java
Normal file
41
java/google/registry/tools/MutatingEppToolCommand.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
// 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;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
|
||||
/**
|
||||
* A command to execute an epp command that intends to mutate objects
|
||||
* (i.e. enables a dry run option).
|
||||
*/
|
||||
abstract class MutatingEppToolCommand extends EppToolCommand {
|
||||
|
||||
@Parameter(
|
||||
names = {"-d", "--dry_run"},
|
||||
description = "Do not actually commit any mutations")
|
||||
boolean dryRun;
|
||||
|
||||
@Override
|
||||
protected boolean isDryRun() {
|
||||
return dryRun;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initEppToolCommand() throws Exception {
|
||||
initMutatingEppToolCommand();
|
||||
}
|
||||
|
||||
abstract void initMutatingEppToolCommand() throws Exception;
|
||||
}
|
22
java/google/registry/tools/PasswordGenerator.java
Normal file
22
java/google/registry/tools/PasswordGenerator.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
// 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;
|
||||
|
||||
/** Password generator interface. */
|
||||
interface PasswordGenerator {
|
||||
|
||||
/** Generates a password of a specified length. */
|
||||
String createPassword(int length);
|
||||
}
|
55
java/google/registry/tools/PendingEscrowCommand.java
Normal file
55
java/google/registry/tools/PendingEscrowCommand.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.domain.registry.rde.PendingDeposit;
|
||||
import com.google.domain.registry.rde.PendingDepositChecker;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to show what escrow deposits are pending generation on the server. */
|
||||
@Parameters(separators = " =", commandDescription = "List pending RDE/BRDA deposits.")
|
||||
final class PendingEscrowCommand implements RemoteApiCommand {
|
||||
|
||||
private static final Ordering<PendingDeposit> SORTER =
|
||||
new Ordering<PendingDeposit>() {
|
||||
@Override
|
||||
public int compare(PendingDeposit left, PendingDeposit right) {
|
||||
return ComparisonChain.start()
|
||||
.compare(left.tld(), right.tld())
|
||||
.compare(left.mode(), right.mode())
|
||||
.compare(left.watermark(), right.watermark())
|
||||
.result();
|
||||
}};
|
||||
|
||||
@Inject
|
||||
PendingDepositChecker checker;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
System.out.println(FluentIterable
|
||||
.from(SORTER.sortedCopy(checker.getTldsAndWatermarksPendingDepositForRdeAndBrda().values()))
|
||||
.transform(Functions.toStringFunction())
|
||||
.join(Joiner.on('\n')));
|
||||
}
|
||||
}
|
104
java/google/registry/tools/PublishDetailReportCommand.java
Normal file
104
java/google/registry/tools/PublishDetailReportCommand.java
Normal file
|
@ -0,0 +1,104 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.export.PublishDetailReportAction;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Command to publish a given billing detail report to a registrar's Drive folder.
|
||||
*/
|
||||
@Parameters(separators = " =", commandDescription = "Publish detail report for a registrar")
|
||||
public class PublishDetailReportCommand extends ConfirmingCommand
|
||||
implements ServerSideCommand, GtechCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--registrar_id",
|
||||
description = "Client identifier of the registrar to publish the report for",
|
||||
required = true)
|
||||
private String registrarId;
|
||||
|
||||
@Parameter(
|
||||
names = "--report_name",
|
||||
description = "Name of the detail report (without directory prefixes)",
|
||||
required = true)
|
||||
private String reportName;
|
||||
|
||||
@Parameter(
|
||||
names = "--gcs_bucket",
|
||||
description = "Name of the GCS bucket that holds billing output files.")
|
||||
private String gcsBucket = "domain-registry-billing";
|
||||
|
||||
@Parameter(
|
||||
names = "--gcs_folder",
|
||||
description = "GCS folder under which report was exported.",
|
||||
required = true)
|
||||
private String gcsFolder;
|
||||
|
||||
private static final String DRIVE_FOLDER_URL_TEMPLATE =
|
||||
"https://drive.google.com/corp/drive/#folders/%s";
|
||||
|
||||
private String gcsFolderPrefix;
|
||||
private String driveFolderUrl;
|
||||
|
||||
private Connection connection;
|
||||
|
||||
@Override
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
// Append a trailing slash to the GCS folder (if there is one, and if it doesn't already end
|
||||
// in a slash) to get the "folder prefix" version.
|
||||
// TODO(b/18611424): Fix PublishDetailReportAction to take fewer error-prone parameters.
|
||||
gcsFolderPrefix =
|
||||
(gcsFolder.isEmpty() || gcsFolder.endsWith("/")) ? gcsFolder : gcsFolder + "/";
|
||||
Registrar registrar = checkNotNull(
|
||||
Registrar.loadByClientId(registrarId), "Registrar with ID %s not found", registrarId);
|
||||
driveFolderUrl = String.format(DRIVE_FOLDER_URL_TEMPLATE, registrar.getDriveFolderId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
String gcsFile = String.format("gs://%s/%s%s", gcsBucket, gcsFolderPrefix, reportName);
|
||||
return "Publish detail report:\n"
|
||||
+ " - Registrar: " + registrarId + "\n"
|
||||
+ " - Drive folder: " + driveFolderUrl + "\n"
|
||||
+ " - GCS file: " + gcsFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
final ImmutableMap<String, String> params = ImmutableMap.of(
|
||||
PublishDetailReportAction.REGISTRAR_ID_PARAM, registrarId,
|
||||
PublishDetailReportAction.DETAIL_REPORT_NAME_PARAM, reportName,
|
||||
PublishDetailReportAction.GCS_FOLDER_PREFIX_PARAM, gcsFolderPrefix,
|
||||
PublishDetailReportAction.GCS_BUCKET_PARAM, gcsBucket);
|
||||
Map<String, Object> response =
|
||||
connection.sendJson(PublishDetailReportAction.PATH, params);
|
||||
return "Success! Published report with drive file ID: " + response.get("driveId");
|
||||
}
|
||||
}
|
46
java/google/registry/tools/RandomPasswordGenerator.java
Normal file
46
java/google/registry/tools/RandomPasswordGenerator.java
Normal file
|
@ -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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Password generator. */
|
||||
class RandomPasswordGenerator implements PasswordGenerator {
|
||||
|
||||
private static final String SYMBOLS =
|
||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-=";
|
||||
|
||||
private final Random random;
|
||||
|
||||
@Inject
|
||||
RandomPasswordGenerator(Random random) {
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
/** Generates a password of a specified length. */
|
||||
@Override
|
||||
public String createPassword(int length) {
|
||||
checkArgument(length > 0);
|
||||
char[] password = new char[length];
|
||||
for (int i = 0; i < length; ++i) {
|
||||
password[i] = SYMBOLS.charAt(random.nextInt(SYMBOLS.length()));
|
||||
}
|
||||
return new String(password);
|
||||
}
|
||||
}
|
116
java/google/registry/tools/RegistrarActivityReportCommand.java
Normal file
116
java/google/registry/tools/RegistrarActivityReportCommand.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.util.ResourceUtils.readResourceUtf8;
|
||||
|
||||
import com.google.api.services.bigquery.model.Job;
|
||||
import com.google.api.services.bigquery.model.JobConfiguration;
|
||||
import com.google.api.services.bigquery.model.JobConfigurationExtract;
|
||||
import com.google.api.services.bigquery.model.JobConfigurationQuery;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.bigquery.BigqueryConnection;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.util.SqlTemplate;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.beust.jcommander.ParametersDelegate;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/** Command for creating a registrar activity report and saving it to cloud storage. */
|
||||
@Parameters(separators = " =", commandDescription = "Generates a registrar activity report.")
|
||||
final class RegistrarActivityReportCommand implements GtechCommand {
|
||||
|
||||
@ParametersDelegate
|
||||
private final BigqueryParameters bigqueryParameters = new BigqueryParameters();
|
||||
|
||||
@Parameter(
|
||||
names = {"-t", "--tld"},
|
||||
description = "Name of TLD (in ASCII) on which to run report.",
|
||||
required = true)
|
||||
private String tld;
|
||||
|
||||
@Parameter(
|
||||
names = {"-r", "--registrar"},
|
||||
description = "Registrar clientId for which we're running this report.",
|
||||
required = true)
|
||||
private String registrarClientId;
|
||||
|
||||
@Parameter(
|
||||
names = {"-s", "--start"},
|
||||
description = "Start time for report.",
|
||||
required = true)
|
||||
private DateTime start;
|
||||
|
||||
@Parameter(
|
||||
names = {"-e", "--end"},
|
||||
description = "End time for report.",
|
||||
required = true)
|
||||
private DateTime end;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "GCS output filename, e.g. gs://bucket/blah.csv",
|
||||
required = true)
|
||||
private Path output;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
checkArgument(output.toUri().getScheme().equals("gs"),
|
||||
"Not a valid cloud storage URI: %s", output);
|
||||
checkArgument(end.isAfter(start), "End time must be after start time");
|
||||
try (BigqueryConnection bq = bigqueryParameters.newConnection()) {
|
||||
bq.ensureTable(bq.getTable("ReportingIdentifiers"),
|
||||
getSql("ReportingIdentifiers.sql")
|
||||
.build());
|
||||
bq.ensureTable(bq.getTable("ReportingHistory"),
|
||||
getSql("ReportingHistory.sql")
|
||||
.build());
|
||||
Job job = bq.runJob(new Job()
|
||||
.setConfiguration(new JobConfiguration()
|
||||
.setQuery(new JobConfigurationQuery()
|
||||
.setDefaultDataset(bq.getDataset())
|
||||
.setQuery(getSql("registrar_activity_report.sql")
|
||||
.put("STARTTIME", BIGQUERY_TIMESTAMP_FORMATTER.print(start))
|
||||
.put("ENDTIME", BIGQUERY_TIMESTAMP_FORMATTER.print(end))
|
||||
.put("REGISTRAR", registrarClientId)
|
||||
.put("TLD", tld)
|
||||
.build()))));
|
||||
bq.runJob(new Job()
|
||||
.setConfiguration(new JobConfiguration()
|
||||
.setExtract(new JobConfigurationExtract()
|
||||
.setPrintHeader(true)
|
||||
.setDestinationFormat("CSV")
|
||||
.setDestinationUris(ImmutableList.of(output.toUri().toString()))
|
||||
.setSourceTable(job.getConfiguration().getQuery().getDestinationTable()))));
|
||||
}
|
||||
}
|
||||
|
||||
private static SqlTemplate getSql(String filename) {
|
||||
return SqlTemplate.create(
|
||||
readResourceUtf8(RegistrarActivityReportCommand.class, "sql/" + filename));
|
||||
}
|
||||
|
||||
/** Bigquery timestamps silently fail if you put the {@code T} in there. */
|
||||
private static final DateTimeFormatter BIGQUERY_TIMESTAMP_FORMATTER =
|
||||
DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss").withZoneUTC();
|
||||
}
|
264
java/google/registry/tools/RegistrarContactCommand.java
Normal file
264
java/google/registry/tools/RegistrarContactCommand.java
Normal file
|
@ -0,0 +1,264 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static com.google.domain.registry.util.CollectionUtils.nullToEmpty;
|
||||
import static com.google.domain.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.domain.registry.model.common.GaeUserIdConverter;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.registrar.RegistrarContact;
|
||||
import com.google.domain.registry.model.registrar.RegistrarContact.Builder;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.params.OptionalPhoneNumberParameter;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Command for CRUD operations on {@link Registrar} contact list fields. */
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription = "Create/read/update/delete the various contact lists for a Registrar.")
|
||||
final class RegistrarContactCommand extends MutatingCommand implements GtechCommand {
|
||||
|
||||
private enum Mode { LIST, CREATE, UPDATE, DELETE }
|
||||
|
||||
@Parameter(
|
||||
description = "Client identifier of the registrar account.",
|
||||
required = true)
|
||||
private List<String> mainParameters;
|
||||
|
||||
@Parameter(
|
||||
names = "--mode",
|
||||
description = "Type of operation you want to perform (LIST, CREATE, UPDATE, or DELETE).",
|
||||
required = true)
|
||||
private Mode mode;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--name",
|
||||
description = "Contact name.")
|
||||
private String name;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--contact_type",
|
||||
description = "Type of communications for this contact; separate multiple with commas."
|
||||
+ " Allowed values are ABUSE, ADMIN, BILLING, LEGAL, MARKETING, TECH, WHOIS.")
|
||||
private List<String> contactTypeNames;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--email",
|
||||
description = "Contact email address.")
|
||||
private String email;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--phone",
|
||||
description = "E.164 phone number, e.g. +1.2125650666",
|
||||
converter = OptionalPhoneNumberParameter.class,
|
||||
validateWith = OptionalPhoneNumberParameter.class)
|
||||
private Optional<String> phone;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--fax",
|
||||
description = "E.164 fax number, e.g. +1.2125650666",
|
||||
converter = OptionalPhoneNumberParameter.class,
|
||||
validateWith = OptionalPhoneNumberParameter.class)
|
||||
private Optional<String> fax;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--allow_console_access",
|
||||
description = "Enable or disable access to the registrar console for this contact.",
|
||||
arity = 1)
|
||||
private Boolean allowConsoleAccess;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--visible_in_whois_as_admin",
|
||||
description = " Whether this contact is publically visible in WHOIS results as an "
|
||||
+ "Admin contact.",
|
||||
arity = 1)
|
||||
private Boolean visibleInWhoisAsAdmin;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--visible_in_whois_as_tech",
|
||||
description = " Whether this contact is publically visible in WHOIS results as a "
|
||||
+ "Tech contact.",
|
||||
arity = 1)
|
||||
private Boolean visibleInWhoisAsTech;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file when --mode=LIST",
|
||||
validateWith = PathParameter.OutputFile.class)
|
||||
private Path output = Paths.get("/dev/stdout");
|
||||
|
||||
@Nullable
|
||||
private Set<RegistrarContact.Type> contactTypes;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
checkArgument(mainParameters.size() == 1,
|
||||
"Must specify exactly one client identifier: %s", ImmutableList.copyOf(mainParameters));
|
||||
String clientIdentifier = mainParameters.get(0);
|
||||
Registrar registrar = checkNotNull(
|
||||
Registrar.loadByClientId(clientIdentifier), "Registrar %s not found", clientIdentifier);
|
||||
contactTypes =
|
||||
newHashSet(
|
||||
transform(
|
||||
nullToEmpty(contactTypeNames), Enums.stringConverter(RegistrarContact.Type.class)));
|
||||
ImmutableSet<RegistrarContact> contacts = registrar.getContacts();
|
||||
Map<String, RegistrarContact> contactsMap = new LinkedHashMap<>();
|
||||
for (RegistrarContact rc : contacts) {
|
||||
contactsMap.put(rc.getEmailAddress(), rc);
|
||||
}
|
||||
RegistrarContact oldContact;
|
||||
switch (mode) {
|
||||
case LIST:
|
||||
listContacts(contacts);
|
||||
break;
|
||||
case CREATE:
|
||||
stageEntityChange(null, createContact(registrar));
|
||||
break;
|
||||
case UPDATE:
|
||||
oldContact =
|
||||
checkNotNull(
|
||||
contactsMap.get(checkNotNull(email, "--email is required when --mode=UPDATE")),
|
||||
"No contact with the given email: %s",
|
||||
email);
|
||||
RegistrarContact newContact = updateContact(oldContact, registrar);
|
||||
stageEntityChange(oldContact, newContact);
|
||||
break;
|
||||
case DELETE:
|
||||
oldContact =
|
||||
checkNotNull(
|
||||
contactsMap.get(checkNotNull(email, "--email is required when --mode=DELETE")),
|
||||
"No contact with the given email: %s",
|
||||
email);
|
||||
stageEntityChange(oldContact, null);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
if (mode == Mode.CREATE || mode == Mode.UPDATE || mode == Mode.DELETE) {
|
||||
stageEntityChange(registrar, registrar.asBuilder().setContactsRequireSyncing(true).build());
|
||||
}
|
||||
}
|
||||
|
||||
private void listContacts(Set<RegistrarContact> contacts) throws IOException {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (RegistrarContact c : contacts) {
|
||||
result.add(c.toStringMultilinePlainText());
|
||||
}
|
||||
Files.write(output, Joiner.on('\n').join(result).getBytes(UTF_8));
|
||||
}
|
||||
|
||||
private RegistrarContact createContact(Registrar registrar) {
|
||||
checkArgument(!isNullOrEmpty(name), "--name is required when --mode=CREATE");
|
||||
checkArgument(!isNullOrEmpty(email), "--email is required when --mode=CREATE");
|
||||
Builder builder = new RegistrarContact.Builder();
|
||||
builder.setParent(registrar);
|
||||
builder.setName(name);
|
||||
builder.setEmailAddress(email);
|
||||
if (phone != null) {
|
||||
builder.setPhoneNumber(phone.orNull());
|
||||
}
|
||||
if (fax != null) {
|
||||
builder.setFaxNumber(fax.orNull());
|
||||
}
|
||||
builder.setTypes(contactTypes);
|
||||
|
||||
if (Objects.equals(allowConsoleAccess, Boolean.TRUE)) {
|
||||
builder.setGaeUserId(checkArgumentNotNull(
|
||||
GaeUserIdConverter.convertEmailAddressToGaeUserId(email),
|
||||
String.format("Email address %s is not associated with any GAE ID", email)));
|
||||
}
|
||||
if (visibleInWhoisAsAdmin != null) {
|
||||
builder.setVisibleInWhoisAsAdmin(visibleInWhoisAsAdmin);
|
||||
}
|
||||
if (visibleInWhoisAsTech != null) {
|
||||
builder.setVisibleInWhoisAsTech(visibleInWhoisAsTech);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private RegistrarContact updateContact(RegistrarContact contact, Registrar registrar) {
|
||||
checkNotNull(registrar);
|
||||
checkNotNull(email, "--email is required when --mode=UPDATE");
|
||||
Builder builder = contact.asBuilder();
|
||||
builder.setParent(registrar);
|
||||
if (!isNullOrEmpty(name)) {
|
||||
builder.setName(name);
|
||||
}
|
||||
if (!isNullOrEmpty(email)) {
|
||||
builder.setEmailAddress(email);
|
||||
}
|
||||
if (phone != null) {
|
||||
builder.setPhoneNumber(phone.orNull());
|
||||
}
|
||||
if (fax != null) {
|
||||
builder.setFaxNumber(fax.orNull());
|
||||
}
|
||||
if (contactTypes != null) {
|
||||
builder.setTypes(contactTypes);
|
||||
}
|
||||
if (visibleInWhoisAsAdmin != null) {
|
||||
builder.setVisibleInWhoisAsAdmin(visibleInWhoisAsAdmin);
|
||||
}
|
||||
if (visibleInWhoisAsTech != null) {
|
||||
builder.setVisibleInWhoisAsTech(visibleInWhoisAsTech);
|
||||
}
|
||||
if (allowConsoleAccess != null) {
|
||||
if (allowConsoleAccess.equals(Boolean.TRUE)) {
|
||||
builder.setGaeUserId(checkArgumentNotNull(
|
||||
GaeUserIdConverter.convertEmailAddressToGaeUserId(email),
|
||||
String.format("Email address %s is not associated with any GAE ID", email)));
|
||||
} else {
|
||||
builder.setGaeUserId(null);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
139
java/google/registry/tools/RegistryCli.java
Normal file
139
java/google/registry/tools/RegistryCli.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.domain.registry.tools.Injector.injectReflectively;
|
||||
|
||||
import com.google.appengine.tools.remoteapi.RemoteApiInstaller;
|
||||
import com.google.appengine.tools.remoteapi.RemoteApiOptions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.model.ofy.ObjectifyService;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.ParameterFactory;
|
||||
|
||||
import com.beust.jcommander.JCommander;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.beust.jcommander.ParametersDelegate;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.security.Security;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Container class to create and run remote commands against a datastore instance. */
|
||||
@Parameters(separators = " =", commandDescription = "Command-line interface to the registry")
|
||||
final class RegistryCli {
|
||||
|
||||
@Parameter(
|
||||
names = {"-e", "--environment"},
|
||||
description = "Sets the default environment to run the command.")
|
||||
private RegistryToolEnvironment environment = RegistryToolEnvironment.PRODUCTION;
|
||||
|
||||
@ParametersDelegate
|
||||
private AppEngineConnection connection = new AppEngineConnection();
|
||||
|
||||
@ParametersDelegate
|
||||
private LoggingParameters loggingParams = new LoggingParameters();
|
||||
|
||||
// The <? extends Class<? extends Command>> wildcard looks a little funny, but is needed so that
|
||||
// we can accept maps with value types that are subtypes of Class<? extends Command> rather than
|
||||
// literally that type (e.g. Class<? extends GtechCommand>). For more explanation, see:
|
||||
// http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ104
|
||||
void run(
|
||||
String programName,
|
||||
String[] args,
|
||||
ImmutableMap<String, ? extends Class<? extends Command>> commands) throws Exception {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
JCommander jcommander = new JCommander(this);
|
||||
jcommander.addConverterFactory(new ParameterFactory());
|
||||
jcommander.setProgramName(programName);
|
||||
|
||||
// Store the instances of each Command class here so we can retrieve the same one for the
|
||||
// called command later on. JCommander could have done this for us, but it doesn't.
|
||||
Map<String, Command> commandInstances = new HashMap<>();
|
||||
|
||||
HelpCommand helpCommand = new HelpCommand(jcommander);
|
||||
jcommander.addCommand("help", helpCommand);
|
||||
commandInstances.put("help", helpCommand);
|
||||
|
||||
for (Map.Entry<String, ? extends Class<? extends Command>> entry : commands.entrySet()) {
|
||||
Command command = entry.getValue().newInstance();
|
||||
jcommander.addCommand(entry.getKey(), command);
|
||||
commandInstances.put(entry.getKey(), command);
|
||||
}
|
||||
|
||||
try {
|
||||
jcommander.parse(args);
|
||||
} catch (ParameterException e) {
|
||||
// If we failed to fully parse the command but at least found a valid command name, show only
|
||||
// the usage for that command. Otherwise, show full usage. Either way, rethrow the error.
|
||||
if (jcommander.getParsedCommand() == null) {
|
||||
jcommander.usage();
|
||||
} else {
|
||||
jcommander.usage(jcommander.getParsedCommand());
|
||||
}
|
||||
// Don't rethrow if we said: registry_tool command --help
|
||||
if ("Unknown option: --help".equals(e.getMessage())) {
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
checkState(RegistryToolEnvironment.get() == environment,
|
||||
"RegistryToolEnvironment argument pre-processing kludge failed.");
|
||||
|
||||
Command command = commandInstances.get(jcommander.getParsedCommand());
|
||||
if (command == null) {
|
||||
jcommander.usage();
|
||||
return;
|
||||
}
|
||||
loggingParams.configureLogging(); // Must be called after parameters are parsed.
|
||||
injectReflectively(RegistryToolComponent.class, DaggerRegistryToolComponent.create(), command);
|
||||
|
||||
if (!(command instanceof RemoteApiCommand)) {
|
||||
command.run();
|
||||
return;
|
||||
}
|
||||
|
||||
if (command instanceof ServerSideCommand) {
|
||||
((ServerSideCommand) command).setConnection(connection);
|
||||
}
|
||||
|
||||
// RemoteApiCommands need to have the remote api installed to work.
|
||||
RemoteApiInstaller installer = new RemoteApiInstaller();
|
||||
RemoteApiOptions options = new RemoteApiOptions();
|
||||
options.server(connection.getServer().getHostText(), connection.getServer().getPort());
|
||||
if (connection.isLocalhost()) {
|
||||
// Use dev credentials for localhost.
|
||||
options.useDevelopmentServerCredential();
|
||||
} else {
|
||||
options.useApplicationDefaultCredential();
|
||||
}
|
||||
installer.install(options);
|
||||
|
||||
// Ensure that all entity classes are loaded before command code runs.
|
||||
ObjectifyService.initOfy();
|
||||
|
||||
try {
|
||||
command.run();
|
||||
} finally {
|
||||
installer.uninstall();
|
||||
}
|
||||
}
|
||||
}
|
81
java/google/registry/tools/RegistryTool.java
Normal file
81
java/google/registry/tools/RegistryTool.java
Normal file
|
@ -0,0 +1,81 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.domain.registry.tools.javascrap.LoadAndResaveCommand;
|
||||
|
||||
/** Container class to create and run remote commands against a datastore instance. */
|
||||
public final class RegistryTool {
|
||||
|
||||
/**
|
||||
* Available commands.
|
||||
*
|
||||
* <p><b>Note:</b> If changing the command-line name of any commands below, remember to resolve
|
||||
* any invocations in scripts (e.g. PDT, ICANN reporting).
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final ImmutableMap<String, Class<? extends Command>> COMMAND_MAP =
|
||||
ImmutableSortedMap.<String, Class<? extends Command>>naturalOrder()
|
||||
.putAll(GtechTool.COMMAND_MAP)
|
||||
.put("allocate_domain", AllocateDomainCommand.class)
|
||||
.put("check_snapshot", CheckSnapshotCommand.class)
|
||||
.put("create_auction_credits", CreateAuctionCreditsCommand.class)
|
||||
.put("create_premium_list", CreatePremiumListCommand.class)
|
||||
.put("create_reserved_list", CreateReservedListCommand.class)
|
||||
.put("create_tld", CreateTldCommand.class)
|
||||
.put("delete_credit", DeleteCreditCommand.class)
|
||||
.put("delete_entity", DeleteEntityCommand.class)
|
||||
.put("delete_epp_resource", DeleteEppResourceCommand.class)
|
||||
.put("delete_premium_list", DeletePremiumListCommand.class)
|
||||
.put("delete_reserved_list", DeleteReservedListCommand.class)
|
||||
.put("encrypt_escrow_deposit", EncryptEscrowDepositCommand.class)
|
||||
.put("execute_epp", ExecuteEppCommand.class)
|
||||
.put("generate_zone_files", GenerateZoneFilesCommand.class)
|
||||
.put("generate_escrow_deposit", GenerateEscrowDepositCommand.class)
|
||||
.put("generate_lordn", GenerateLordnCommand.class)
|
||||
.put("get_claims_list", GetClaimsListCommand.class)
|
||||
.put("get_resource_by_key", GetResourceByKeyCommand.class)
|
||||
.put("ghostryde", GhostrydeCommand.class)
|
||||
.put("list_cursors", ListCursorsCommand.class)
|
||||
.put("list_domains", ListDomainsCommand.class)
|
||||
.put("list_hosts", ListHostsCommand.class)
|
||||
.put("list_premium_lists", ListPremiumListsCommand.class)
|
||||
.put("list_reserved_lists", ListReservedListsCommand.class)
|
||||
.put("load_and_resave", LoadAndResaveCommand.class)
|
||||
.put("load_snapshot", LoadSnapshotCommand.class)
|
||||
.put("make_billing_tables", MakeBillingTablesCommand.class)
|
||||
.put("pending_escrow", PendingEscrowCommand.class)
|
||||
.put("resave_environment_entities", ResaveEnvironmentEntitiesCommand.class)
|
||||
.put("send_escrow_report_to_icann", SendEscrowReportToIcannCommand.class)
|
||||
.put("update_application_status", UpdateApplicationStatusCommand.class)
|
||||
.put("update_claims_notice", UpdateClaimsNoticeCommand.class)
|
||||
.put("update_credits", UpdateCreditsCommand.class)
|
||||
.put("update_cursors", UpdateCursorsCommand.class)
|
||||
.put("update_premium_list", UpdatePremiumListCommand.class)
|
||||
.put("update_reserved_list", UpdateReservedListCommand.class)
|
||||
.put("update_smd", UpdateSmdCommand.class)
|
||||
.put("update_tld", UpdateTldCommand.class)
|
||||
.put("upload_claims_list", UploadClaimsListCommand.class)
|
||||
.put("validate_escrow_deposit", ValidateEscrowDepositCommand.class)
|
||||
.build();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
RegistryToolEnvironment.parseFromArgs(args).setup();
|
||||
new RegistryCli().run("registry_tool", args, COMMAND_MAP);
|
||||
}
|
||||
}
|
59
java/google/registry/tools/RegistryToolComponent.java
Normal file
59
java/google/registry/tools/RegistryToolComponent.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;
|
||||
|
||||
import com.google.domain.registry.config.ConfigModule;
|
||||
import com.google.domain.registry.keyring.api.KeyModule;
|
||||
import com.google.domain.registry.keyring.api.VoidKeyringModule;
|
||||
import com.google.domain.registry.request.Modules.DatastoreServiceModule;
|
||||
import com.google.domain.registry.request.Modules.Jackson2Module;
|
||||
import com.google.domain.registry.request.Modules.URLFetchServiceModule;
|
||||
import com.google.domain.registry.util.SystemClock.SystemClockModule;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
/**
|
||||
* Dagger component for Registry Tool.
|
||||
*
|
||||
* <p>Any command class with {@code @Inject} fields <i>must</i> be listed as a method here.
|
||||
* Otherwise {@link RegistryCli} will not be able to populate those fields after its instantiation.
|
||||
*/
|
||||
@Component(
|
||||
modules = {
|
||||
ConfigModule.class,
|
||||
DatastoreServiceModule.class,
|
||||
Jackson2Module.class,
|
||||
KeyModule.class,
|
||||
RegistryToolModule.class,
|
||||
SystemClockModule.class,
|
||||
URLFetchServiceModule.class,
|
||||
VoidKeyringModule.class,
|
||||
})
|
||||
interface RegistryToolComponent {
|
||||
void inject(CreateAnchorTenantCommand command);
|
||||
void inject(CreateContactCommand command);
|
||||
void inject(EncryptEscrowDepositCommand command);
|
||||
void inject(GenerateApplicationsReportCommand command);
|
||||
void inject(GenerateDnsReportCommand command);
|
||||
void inject(GenerateEscrowDepositCommand command);
|
||||
void inject(GhostrydeCommand command);
|
||||
void inject(ListCursorsCommand command);
|
||||
void inject(PendingEscrowCommand command);
|
||||
void inject(SendEscrowReportToIcannCommand command);
|
||||
void inject(SetupOteCommand command);
|
||||
void inject(UpdateCursorsCommand command);
|
||||
void inject(ValidateEscrowDepositCommand command);
|
||||
void inject(WhoisQueryCommand command);
|
||||
}
|
116
java/google/registry/tools/RegistryToolEnvironment.java
Normal file
116
java/google/registry/tools/RegistryToolEnvironment.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
|
||||
/** Enum of production environments, used for the {@code --environment} flag. */
|
||||
enum RegistryToolEnvironment {
|
||||
PRODUCTION(RegistryEnvironment.PRODUCTION),
|
||||
ALPHA(RegistryEnvironment.ALPHA),
|
||||
CRASH(RegistryEnvironment.CRASH),
|
||||
QA(RegistryEnvironment.QA),
|
||||
SANDBOX(RegistryEnvironment.SANDBOX),
|
||||
LOCALHOST(RegistryEnvironment.LOCAL),
|
||||
UNITTEST(RegistryEnvironment.UNITTEST),
|
||||
PDT(RegistryEnvironment.PRODUCTION, ImmutableMap.of(
|
||||
"com.google.domain.registry.rde.key.receiver",
|
||||
"pdt-escrow-test@icann.org"));
|
||||
|
||||
private static final ImmutableList<String> FLAGS = ImmutableList.of("-e", "--environment");
|
||||
private static RegistryToolEnvironment instance;
|
||||
private final RegistryEnvironment actualEnvironment;
|
||||
private final ImmutableMap<String, String> extraProperties;
|
||||
|
||||
private RegistryToolEnvironment(
|
||||
RegistryEnvironment actualEnvironment,
|
||||
ImmutableMap<String, String> extraProperties) {
|
||||
this.actualEnvironment = actualEnvironment;
|
||||
this.extraProperties = extraProperties;
|
||||
}
|
||||
|
||||
private RegistryToolEnvironment(RegistryEnvironment actualEnvironment) {
|
||||
this(actualEnvironment, ImmutableMap.<String, String>of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts environment from command-line arguments.
|
||||
*
|
||||
* <p>This operation can't be performed by JCommander because it needs to happen before any
|
||||
* command classes get loaded. This is because this routine will set system properties that are
|
||||
* referenced by static initializers for many classes. This approach also allows us to change the
|
||||
* default values of JCommander parameters, based on the selected environment.
|
||||
*
|
||||
* @see #get()
|
||||
*/
|
||||
static RegistryToolEnvironment parseFromArgs(String[] args) {
|
||||
return valueOf(getFlagValue(args, FLAGS).toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current environment.
|
||||
*
|
||||
* <p>This should be called after {@link #parseFromArgs(String[])}.
|
||||
*/
|
||||
static RegistryToolEnvironment get() {
|
||||
checkState(instance != null, "No RegistryToolEnvironment has been set up");
|
||||
return instance;
|
||||
}
|
||||
|
||||
/** Setup execution environment. Call this method before any classes are loaded. */
|
||||
void setup() {
|
||||
instance = this;
|
||||
System.setProperty(RegistryEnvironment.PROPERTY, actualEnvironment.name());
|
||||
for (ImmutableMap.Entry<String, String> entry : extraProperties.entrySet()) {
|
||||
System.setProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/** Extracts value from command-line arguments associated with any {@code flags}. */
|
||||
private static String getFlagValue(String[] args, Iterable<String> flags) {
|
||||
for (String flag : flags) {
|
||||
boolean expecting = false;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
// XXX: Kludge to stop processing once we reach what is *probably* the command name.
|
||||
// This is necessary in order to allow commands to accept arguments named '-e'.
|
||||
// This code should probably be updated, should any zero arity flags be added to
|
||||
// RegistryCli, LoggingParameters, or AppEngineConnection.
|
||||
if (args[i].startsWith("-")) {
|
||||
expecting = !args[i].contains("=");
|
||||
} else {
|
||||
if (expecting) {
|
||||
expecting = false;
|
||||
} else {
|
||||
break; // This is the command name, unless zero arity flags were added.
|
||||
}
|
||||
}
|
||||
if (args[i].equals(flag)) {
|
||||
checkArgument(i + 1 < args.length, "%s flag missing value.", flag);
|
||||
return args[i + 1];
|
||||
}
|
||||
if (args[i].startsWith(flag + "=")
|
||||
|| args[i].startsWith(flag + " ")) {
|
||||
return args[i].substring(flag.length() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Please specify the environment flag, e.g. -e production.");
|
||||
}
|
||||
}
|
47
java/google/registry/tools/RegistryToolModule.java
Normal file
47
java/google/registry/tools/RegistryToolModule.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
// 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;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
|
||||
/** Dagger module for Registry Tool. */
|
||||
@Module
|
||||
final class RegistryToolModule {
|
||||
|
||||
@Provides
|
||||
static RegistryToolEnvironment provideRegistryToolEnvironment() {
|
||||
return RegistryToolEnvironment.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
static PasswordGenerator providePasswordGenerator(RandomPasswordGenerator passwordGenerator) {
|
||||
return passwordGenerator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
static Random provideRandom() {
|
||||
try {
|
||||
return SecureRandom.getInstance("NativePRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ProviderException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import static com.google.domain.registry.model.common.EntityGroupRoot.getCrossTldKey;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.domain.registry.model.ImmutableObject;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.registrar.RegistrarContact;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.Key;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
||||
/**
|
||||
* Command to re-save all environment entities to ensure that they have valid commit logs.
|
||||
*
|
||||
* <p>The entities that are re-saved are those of type {@link Registry}, {@link Registrar}, and
|
||||
* {@link RegistrarContact}.
|
||||
*/
|
||||
@Parameters(commandDescription = "Re-save all environment entities.")
|
||||
final class ResaveEnvironmentEntitiesCommand implements RemoteApiCommand {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
Iterable<Key<? extends ImmutableObject>> keys = concat(
|
||||
ofy().load().type(Registrar.class).ancestor(getCrossTldKey()).keys(),
|
||||
ofy().load().type(Registry.class).ancestor(getCrossTldKey()).keys(),
|
||||
ofy().load().type(RegistrarContact.class).ancestor(getCrossTldKey()).keys());
|
||||
for (final Key<? extends ImmutableObject> key : keys) {
|
||||
ofy().transact(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
ofy().save().entity(ofy().load().key(key).now());
|
||||
}});
|
||||
System.out.printf("Re-saved entity %s\n", key);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// 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;
|
||||
|
||||
import com.google.domain.registry.rde.RdeReporter;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to send ICANN notification that an escrow deposit was uploaded. */
|
||||
@Parameters(separators = " =", commandDescription = "Send an ICANN report of an uploaded deposit.")
|
||||
final class SendEscrowReportToIcannCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "One or more foo-report.xml files.",
|
||||
validateWith = PathParameter.InputFile.class,
|
||||
required = true)
|
||||
private List<Path> files;
|
||||
|
||||
@Inject
|
||||
RdeReporter rdeReporter;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
for (Path file : files) {
|
||||
rdeReporter.send(Files.readAllBytes(file));
|
||||
System.out.printf("Uploaded: %s\n", file);
|
||||
}
|
||||
}
|
||||
}
|
40
java/google/registry/tools/ServerSideCommand.java
Normal file
40
java/google/registry/tools/ServerSideCommand.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/** A command that executes on the server. */
|
||||
interface ServerSideCommand extends RemoteApiCommand {
|
||||
|
||||
/** An http connection to AppEngine. */
|
||||
interface Connection {
|
||||
|
||||
void prefetchXsrfToken() throws IOException;
|
||||
|
||||
String send(String endpoint, Map<String, ?> params, MediaType contentType, byte[] payload)
|
||||
throws IOException;
|
||||
|
||||
Map<String, Object> sendJson(String endpoint, Map<String, ?> object) throws IOException;
|
||||
|
||||
String getServerUrl();
|
||||
}
|
||||
|
||||
void setConnection(Connection connection);
|
||||
}
|
223
java/google/registry/tools/SetupOteCommand.java
Normal file
223
java/google/registry/tools/SetupOteCommand.java
Normal file
|
@ -0,0 +1,223 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.tools.CommandUtilities.promptForYes;
|
||||
import static com.google.domain.registry.util.X509Utils.loadCertificate;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.config.RegistryEnvironment;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.registry.Registry.TldState;
|
||||
import com.google.domain.registry.tools.Command.GtechCommand;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
import com.google.domain.registry.tools.params.PathParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Composite command to set up OT&E TLDs and accounts. */
|
||||
@Parameters(
|
||||
separators = " =",
|
||||
commandDescription = "Set up OT&E TLDs and registrars")
|
||||
final class SetupOteCommand extends ConfirmingCommand implements RemoteApiCommand, GtechCommand {
|
||||
|
||||
// Regex: 3-14 alphanumeric characters or hyphens, the first of which must be a letter.
|
||||
private static final Pattern REGISTRAR_PATTERN = Pattern.compile("^[a-z][-a-z0-9]{2,13}$");
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
|
||||
// Durations are short so that registrars can test with quick transfer (etc.) turnaround.
|
||||
private static final Duration SHORT_ADD_GRACE_PERIOD = Duration.standardMinutes(60);
|
||||
private static final Duration SHORT_REDEMPTION_GRACE_PERIOD = Duration.standardMinutes(10);
|
||||
private static final Duration SHORT_PENDING_DELETE_LENGTH = Duration.standardMinutes(5);
|
||||
|
||||
private static final String DEFAULT_PREMIUM_LIST = "default_sandbox_list";
|
||||
|
||||
@Parameter(
|
||||
names = {"-r", "--registrar"},
|
||||
description = "must 1) consist of only lowercase letters, numbers, or hyphens, "
|
||||
+ "2) start with a letter, and 3) be between 3 and 14 characters (inclusive). "
|
||||
+ "We require 1 and 2 since the registrar name will be used to create TLDs,"
|
||||
+ "and we require 3 since we append \"-[1234]\" to the name to create client"
|
||||
+ "IDs which are required by the EPP XML schema to be between 3-16 chars.",
|
||||
required = true)
|
||||
private String registrar;
|
||||
|
||||
@Parameter(
|
||||
names = {"-w", "--ip_whitelist"},
|
||||
description = "comma separated list of IP addreses or CIDR ranges",
|
||||
required = true)
|
||||
private List<String> ipWhitelist = new ArrayList<>();
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--certfile"},
|
||||
description = "full path to cert file in PEM format (best if on local storage)",
|
||||
required = true,
|
||||
validateWith = PathParameter.InputFile.class)
|
||||
private Path certFile;
|
||||
|
||||
@Parameter(
|
||||
names = {"--premium_list"},
|
||||
description = "premium list to apply to all TLDs")
|
||||
private String premiumList = DEFAULT_PREMIUM_LIST;
|
||||
|
||||
@Inject
|
||||
PasswordGenerator passwordGenerator;
|
||||
|
||||
/**
|
||||
* Long registrar names are truncated and then have an incrementing digit appended at the end so
|
||||
* that unique ROID suffixes can be generated for all TLDs for the registrar.
|
||||
*/
|
||||
private int roidSuffixCounter = 0;
|
||||
|
||||
/** Constructs and runs a CreateTldCommand. */
|
||||
private void createTld(
|
||||
String tldName,
|
||||
TldState initialTldState,
|
||||
Duration addGracePeriod,
|
||||
Duration redemptionGracePeriod,
|
||||
Duration pendingDeleteLength) throws Exception {
|
||||
CreateTldCommand command = new CreateTldCommand();
|
||||
command.initialTldState = initialTldState;
|
||||
command.mainParameters = ImmutableList.of(tldName);
|
||||
command.roidSuffix = String.format(
|
||||
"%S%X", tldName.replaceAll("[^a-z0-9]", "").substring(0, 7), roidSuffixCounter++);
|
||||
command.addGracePeriod = addGracePeriod;
|
||||
command.redemptionGracePeriod = redemptionGracePeriod;
|
||||
command.pendingDeleteLength = pendingDeleteLength;
|
||||
command.premiumListName = Optional.of(premiumList);
|
||||
command.force = force;
|
||||
command.run();
|
||||
}
|
||||
|
||||
/** Constructs and runs a CreateRegistrarCommand */
|
||||
private void createRegistrar(String registrarName, String password, String tld) throws Exception {
|
||||
CreateRegistrarCommand command = new CreateRegistrarCommand();
|
||||
command.mainParameters = ImmutableList.of(registrarName);
|
||||
command.createGoogleGroups = false; // Don't create Google Groups for OT&E registrars.
|
||||
command.allowedTlds = ImmutableList.of(tld);
|
||||
command.registrarName = registrarName;
|
||||
command.registrarType = Registrar.Type.OTE;
|
||||
command.password = password;
|
||||
command.clientCertificateFilename = certFile;
|
||||
command.ipWhitelist = ipWhitelist;
|
||||
command.street = ImmutableList.of("e-street");
|
||||
command.city = "Neverland";
|
||||
command.state = "ofmind";
|
||||
command.countryCode = "US";
|
||||
command.zip = "55555";
|
||||
command.email = Optional.of("foo@neverland.com");
|
||||
command.fax = Optional.of("+1.2125550100");
|
||||
command.phone = Optional.of("+1.2125550100");
|
||||
command.icannReferralEmail = "nightmare@registrar.test";
|
||||
command.force = force;
|
||||
command.run();
|
||||
}
|
||||
|
||||
/** Run any pre-execute command checks */
|
||||
@Override
|
||||
protected boolean checkExecutionState() throws Exception {
|
||||
checkArgument(REGISTRAR_PATTERN.matcher(registrar).matches(),
|
||||
"Registrar name is invalid (see usage text for requirements).");
|
||||
|
||||
boolean warned = false;
|
||||
if (RegistryEnvironment.get() != RegistryEnvironment.SANDBOX
|
||||
&& RegistryEnvironment.get() != RegistryEnvironment.UNITTEST) {
|
||||
System.err.printf(
|
||||
"WARNING: Running against %s environment. Are "
|
||||
+ "you sure you didn\'t mean to run this against sandbox (e.g. \"-e SANDBOX\")?%n",
|
||||
RegistryEnvironment.get());
|
||||
warned = true;
|
||||
}
|
||||
|
||||
if (warned && !promptForYes("Proceed despite warnings?")) {
|
||||
System.out.println("Command aborted.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't wait for create_registrar to fail if it's a bad certificate file.
|
||||
loadCertificate(certFile.toAbsolutePath());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() throws Exception {
|
||||
// Each underlying command will confirm its own operation as well, so just provide
|
||||
// a summary of the steps in this command.
|
||||
return "Creating TLDs:\n"
|
||||
+ " " + registrar + "-sunrise\n"
|
||||
+ " " + registrar + "-landrush\n"
|
||||
+ " " + registrar + "-ga\n"
|
||||
+ "Creating registrars:\n"
|
||||
+ " " + registrar + "-1 (access to TLD " + registrar + "-sunrise)\n"
|
||||
+ " " + registrar + "-2 (access to TLD " + registrar + "-landrush)\n"
|
||||
+ " " + registrar + "-3 (access to TLD " + registrar + "-ga)\n"
|
||||
+ " " + registrar + "-4 (access to TLD " + registrar + "-ga)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute() throws Exception {
|
||||
createTld(registrar + "-sunrise", TldState.SUNRISE, null, null, null);
|
||||
createTld(registrar + "-landrush", TldState.LANDRUSH, null, null, null);
|
||||
createTld(
|
||||
registrar + "-ga",
|
||||
TldState.GENERAL_AVAILABILITY,
|
||||
SHORT_ADD_GRACE_PERIOD,
|
||||
SHORT_REDEMPTION_GRACE_PERIOD,
|
||||
SHORT_PENDING_DELETE_LENGTH);
|
||||
|
||||
// Storing names and credentials in a list of tuples for later play-back.
|
||||
List<List<String>> registrars = new ArrayList<>();
|
||||
registrars.add(ImmutableList.<String>of(
|
||||
registrar + "-1", passwordGenerator.createPassword(PASSWORD_LENGTH),
|
||||
registrar + "-sunrise"));
|
||||
registrars.add(ImmutableList.<String>of(
|
||||
registrar + "-2", passwordGenerator.createPassword(PASSWORD_LENGTH),
|
||||
registrar + "-landrush"));
|
||||
registrars.add(ImmutableList.<String>of(
|
||||
registrar + "-3", passwordGenerator.createPassword(PASSWORD_LENGTH),
|
||||
registrar + "-ga"));
|
||||
registrars.add(ImmutableList.<String>of(
|
||||
registrar + "-4", passwordGenerator.createPassword(PASSWORD_LENGTH),
|
||||
registrar + "-ga"));
|
||||
|
||||
for (List<String> r : registrars) {
|
||||
createRegistrar(r.get(0), r.get(1), r.get(2));
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
output.append("Copy these usernames/passwords back into the onboarding bug:\n\n");
|
||||
|
||||
for (List<String> r : registrars) {
|
||||
output.append("Login: " + r.get(0) + "\n");
|
||||
output.append("Password: " + r.get(1) + "\n");
|
||||
output.append("TLD: " + r.get(2) + "\n\n");
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
}
|
183
java/google/registry/tools/UpdateApplicationStatusCommand.java
Normal file
183
java/google/registry/tools/UpdateApplicationStatusCommand.java
Normal file
|
@ -0,0 +1,183 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
import static com.google.domain.registry.model.domain.launch.ApplicationStatus.ALLOCATED;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static com.google.domain.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.launch.ApplicationStatus;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchInfoResponseExtension;
|
||||
import com.google.domain.registry.model.eppcommon.StatusValue;
|
||||
import com.google.domain.registry.model.eppcommon.Trid;
|
||||
import com.google.domain.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
|
||||
import com.google.domain.registry.model.poll.PollMessage;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Command to manually update the status of a domain application. */
|
||||
@Parameters(separators = " =", commandDescription = "Manually update domain application status.")
|
||||
final class UpdateApplicationStatusCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--ids",
|
||||
description = "Comma-delimited list of application IDs to update the status.",
|
||||
required = true)
|
||||
private List<String> ids;
|
||||
|
||||
@Parameter(
|
||||
names = "--msg",
|
||||
description = "Message shown to registrars in the poll message.",
|
||||
required = true)
|
||||
private String message;
|
||||
|
||||
@Parameter(
|
||||
names = "--status",
|
||||
description = "The new application status.",
|
||||
required = true)
|
||||
private ApplicationStatus newStatus;
|
||||
|
||||
@Parameter(
|
||||
names = "--history_client_id",
|
||||
description = "Client id that made this change. Only recorded in the history entry.")
|
||||
private String clientId = "CharlestonRoad";
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
checkArgumentNotNull(
|
||||
Registrar.loadByClientId(clientId), "Registrar with client ID %s not found", clientId);
|
||||
for (final String applicationId : ids) {
|
||||
ofy().transact(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
updateApplicationStatus(applicationId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stages changes to update the status of an application and also enqueue a poll message for the
|
||||
* status change, which may contain a PendingActionNotificationResponse if this is a final status.
|
||||
* <p>
|
||||
* This method must be called from within a transaction.
|
||||
*/
|
||||
private void updateApplicationStatus(String applicationId) {
|
||||
ofy().assertInTransaction();
|
||||
DateTime now = ofy().getTransactionTime();
|
||||
|
||||
// Load the domain application.
|
||||
DomainApplication domainApplication =
|
||||
loadByUniqueId(DomainApplication.class, applicationId, now);
|
||||
checkArgumentNotNull(domainApplication, "Domain application does not exist");
|
||||
|
||||
// It's not an error if the application already has the intended status. We want the method
|
||||
// to be idempotent.
|
||||
if (domainApplication.getApplicationStatus() == newStatus) {
|
||||
System.err.printf("Domain application %s already has status %s\n", applicationId, newStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure domain does not already have a final status which it should not be updated from.
|
||||
checkState(
|
||||
!domainApplication.getApplicationStatus().isFinalStatus(),
|
||||
"Domain application has final status %s",
|
||||
domainApplication.getApplicationStatus());
|
||||
|
||||
// Update its status.
|
||||
DomainApplication.Builder applicationBuilder = domainApplication.asBuilder()
|
||||
.setApplicationStatus(newStatus)
|
||||
.setLastEppUpdateTime(now)
|
||||
// Use the current sponsor instead of the history client ID because the latter might be an
|
||||
// internal client ID that we don't want to expose.
|
||||
.setLastEppUpdateClientId(domainApplication.getCurrentSponsorClientId());
|
||||
|
||||
// Create a history entry (with no XML or Trid) to record that we are updating the status on
|
||||
// this application.
|
||||
HistoryEntry newHistoryEntry = new HistoryEntry.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_STATUS_UPDATE)
|
||||
.setParent(domainApplication)
|
||||
.setModificationTime(now)
|
||||
.setClientId(clientId)
|
||||
.setBySuperuser(true)
|
||||
.build();
|
||||
|
||||
// Create a poll message informing the registrar that the application status was updated.
|
||||
PollMessage.OneTime.Builder pollMessageBuilder = new PollMessage.OneTime.Builder()
|
||||
.setClientId(domainApplication.getCurrentSponsorClientId())
|
||||
.setEventTime(ofy().getTransactionTime())
|
||||
.setMsg(message)
|
||||
.setParent(newHistoryEntry)
|
||||
.setResponseExtensions(ImmutableList.of(
|
||||
new LaunchInfoResponseExtension.Builder()
|
||||
.setApplicationId(domainApplication.getForeignKey())
|
||||
.setPhase(domainApplication.getPhase())
|
||||
.setApplicationStatus(newStatus)
|
||||
.build()));
|
||||
|
||||
// If this is a final status (i.e. an end state), then we should remove pending create from the
|
||||
// application and include a pending action notification in the poll message. Conversely, if
|
||||
// this is not a final status, we should add pending create (which will already be there unless
|
||||
// we're resurrecting an application).
|
||||
if (newStatus.isFinalStatus()) {
|
||||
applicationBuilder.removeStatusValue(StatusValue.PENDING_CREATE);
|
||||
|
||||
pollMessageBuilder.setResponseData(ImmutableList.of(
|
||||
DomainPendingActionNotificationResponse.create(
|
||||
domainApplication.getFullyQualifiedDomainName(),
|
||||
ALLOCATED.equals(newStatus), // Whether the operation succeeded
|
||||
getCreationTrid(domainApplication),
|
||||
now)));
|
||||
} else {
|
||||
applicationBuilder.addStatusValue(StatusValue.PENDING_CREATE);
|
||||
}
|
||||
|
||||
// Stage changes for all entities that need to be saved to datastore.
|
||||
stageEntityChange(domainApplication, applicationBuilder.build());
|
||||
stageEntityChange(null, pollMessageBuilder.build());
|
||||
stageEntityChange(null, newHistoryEntry);
|
||||
}
|
||||
|
||||
/** Retrieve the transaction id of the operation which created this application. */
|
||||
static Trid getCreationTrid(DomainApplication domainApplication) {
|
||||
Trid creationTrid = domainApplication.getCreationTrid();
|
||||
if (creationTrid == null) {
|
||||
// If the creation TRID is not present on the application (this can happen for older
|
||||
// applications written before this field was added), then we must read the earliest history
|
||||
// entry for the application to retrieve it.
|
||||
return checkNotNull(checkNotNull(ofy()
|
||||
.load()
|
||||
.type(HistoryEntry.class)
|
||||
.ancestor(domainApplication)
|
||||
.order("modificationTime")
|
||||
.first()
|
||||
.now()).getTrid());
|
||||
}
|
||||
return creationTrid;
|
||||
}
|
||||
}
|
120
java/google/registry/tools/UpdateClaimsNoticeCommand.java
Normal file
120
java/google/registry/tools/UpdateClaimsNoticeCommand.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.model.domain.DomainApplication;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchNotice;
|
||||
import com.google.domain.registry.model.domain.launch.LaunchNotice.InvalidChecksumException;
|
||||
import com.google.domain.registry.model.reporting.HistoryEntry;
|
||||
import com.google.domain.registry.tools.Command.RemoteApiCommand;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.googlecode.objectify.VoidWork;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Command to update the claims notice on a domain application. */
|
||||
@Parameters(separators = " =", commandDescription = "Update the claims notice on an application.")
|
||||
final class UpdateClaimsNoticeCommand implements RemoteApiCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--id",
|
||||
description = "Application ID to update.",
|
||||
required = true)
|
||||
private String id;
|
||||
|
||||
@Parameter(
|
||||
names = "--tcn_id",
|
||||
description = "Trademark claims notice ID.",
|
||||
required = true)
|
||||
private String tcnId;
|
||||
|
||||
@Parameter(
|
||||
names = "--validator_id",
|
||||
description = "Trademark claims validator.")
|
||||
private String validatorId = "tmch";
|
||||
|
||||
@Parameter(
|
||||
names = "--expiration_time",
|
||||
description = "Expiration time of claims notice.",
|
||||
required = true)
|
||||
private String expirationTime;
|
||||
|
||||
@Parameter(
|
||||
names = "--accepted_time",
|
||||
description = "Accepted time of claims notice.",
|
||||
required = true)
|
||||
private String acceptedTime;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
final LaunchNotice launchNotice = LaunchNotice.create(
|
||||
tcnId, validatorId, DateTime.parse(expirationTime), DateTime.parse(acceptedTime));
|
||||
|
||||
ofy().transact(new VoidWork() {
|
||||
@Override
|
||||
public void vrun() {
|
||||
try {
|
||||
updateClaimsNotice(id, launchNotice);
|
||||
} catch (InvalidChecksumException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}});
|
||||
}
|
||||
|
||||
private void updateClaimsNotice(String applicationId, LaunchNotice launchNotice)
|
||||
throws InvalidChecksumException {
|
||||
ofy().assertInTransaction();
|
||||
DateTime now = ofy().getTransactionTime();
|
||||
|
||||
// Load the domain application.
|
||||
DomainApplication domainApplication =
|
||||
loadByUniqueId(DomainApplication.class, applicationId, now);
|
||||
checkArgument(domainApplication != null, "Domain application does not exist");
|
||||
|
||||
// Make sure this isn't a sunrise application.
|
||||
checkArgument(domainApplication.getEncodedSignedMarks().isEmpty(),
|
||||
"Can't update claims notice on sunrise applications.");
|
||||
|
||||
// Validate the new launch notice checksum.
|
||||
String domainLabel = InternetDomainName.from(domainApplication.getFullyQualifiedDomainName())
|
||||
.parts().get(0);
|
||||
launchNotice.validate(domainLabel);
|
||||
|
||||
DomainApplication updatedApplication = domainApplication.asBuilder()
|
||||
.setLaunchNotice(launchNotice)
|
||||
.setLastEppUpdateTime(now)
|
||||
.setLastEppUpdateClientId(domainApplication.getCurrentSponsorClientId())
|
||||
.build();
|
||||
|
||||
// Create a history entry (with no XML or Trid) to record that we are updating the application.
|
||||
HistoryEntry newHistoryEntry = new HistoryEntry.Builder()
|
||||
.setType(HistoryEntry.Type.DOMAIN_APPLICATION_UPDATE)
|
||||
.setParent(domainApplication)
|
||||
.setModificationTime(now)
|
||||
.setClientId(domainApplication.getCurrentSponsorClientId())
|
||||
.setBySuperuser(true)
|
||||
.build();
|
||||
|
||||
// Save entities to datastore.
|
||||
ofy().save().<Object>entities(updatedApplication, newHistoryEntry);
|
||||
}
|
||||
}
|
142
java/google/registry/tools/UpdateCreditsCommand.java
Normal file
142
java/google/registry/tools/UpdateCreditsCommand.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
// 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;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.domain.registry.bigquery.BigqueryUtils.fromBigqueryTimestampString;
|
||||
import static com.google.domain.registry.model.ofy.ObjectifyService.ofy;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.domain.registry.model.billing.RegistrarCredit;
|
||||
import com.google.domain.registry.model.billing.RegistrarCreditBalance;
|
||||
import com.google.domain.registry.model.billing.RegistrarCreditBalance.BalanceMap;
|
||||
import com.google.domain.registry.model.registrar.Registrar;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.money.Money;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
/** Command for writing out new balances of credits updated in an invoicing cycle. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a set of registrar credit balances")
|
||||
final class UpdateCreditsCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
names = "--input_file",
|
||||
description = "CSV file of updated credit balance information with this row format: "
|
||||
+ " <registrar_id>,<credit_id>,<old_balance>,<new_balance>,<effective_time> "
|
||||
+ "(typically produced by running the generate_invoice command)",
|
||||
required = true)
|
||||
private Path inputFile;
|
||||
|
||||
/** Struct-like container for an individual credit's balance update information. */
|
||||
private static class CreditBalanceUpdate {
|
||||
/** The id of the registrar to whom the credit being updated belongs. */
|
||||
final String registrarId;
|
||||
|
||||
/** The id of the credit being updated (i.e. having a new balance written out). */
|
||||
final Long creditId;
|
||||
|
||||
/** The old balance amount of the credit in minor units of the credit's currency. */
|
||||
final Integer oldBalance;
|
||||
|
||||
/** The new balance amount of the credit in minor units of the credit's currency. */
|
||||
final Integer newBalance;
|
||||
|
||||
/** The exact moment at which the new balance becomes active, replacing the old balance. */
|
||||
final DateTime effectiveTime;
|
||||
|
||||
public CreditBalanceUpdate(List<String> fields)
|
||||
throws IllegalArgumentException {
|
||||
checkArgument(fields.size() == 5, "Wrong number of fields provided: %s", fields);
|
||||
this.registrarId = fields.get(0);
|
||||
this.creditId = Long.valueOf(fields.get(1));
|
||||
this.oldBalance = Integer.valueOf(fields.get(2));
|
||||
this.newBalance = Integer.valueOf(fields.get(3));
|
||||
this.effectiveTime = fromBigqueryTimestampString(fields.get(4));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
DateTime now = DateTime.now(UTC);
|
||||
for (String line : Files.readAllLines(inputFile, StandardCharsets.UTF_8)) {
|
||||
List<String> fields = Splitter.on(',').splitToList(line);
|
||||
try {
|
||||
stageCreditUpdate(new CreditBalanceUpdate(fields), now);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException("Cannot parse fields in line: " + line, e);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
/** Stage the actual new balance creation corresponding to the given credit balance update. */
|
||||
private void stageCreditUpdate(CreditBalanceUpdate update, DateTime now) {
|
||||
System.out.printf(
|
||||
"Creating balance update for credit %s/%d at %s:\n",
|
||||
update.registrarId,
|
||||
update.creditId,
|
||||
update.effectiveTime.toString());
|
||||
|
||||
// Load the registrar and the credit, checking for non-existence.
|
||||
Registrar registrar = checkNotNull(Registrar.loadByClientId(update.registrarId),
|
||||
"Registrar %s not found", update.registrarId);
|
||||
RegistrarCredit credit = ofy().load()
|
||||
.type(RegistrarCredit.class)
|
||||
.parent(registrar)
|
||||
.id(update.creditId)
|
||||
.now();
|
||||
checkNotNull(credit,
|
||||
"Registrar credit for %s with ID %s not found",
|
||||
update.registrarId, update.creditId.toString());
|
||||
System.out.printf(" - Credit info: %s\n", credit.getSummary());
|
||||
|
||||
// Load the actual old balance at the moment before the update's effective time and ensure
|
||||
// that it matches the expected old balance amount passed along in the update.
|
||||
Optional<Money> oldBalance =
|
||||
BalanceMap.createForCredit(credit).getActiveBalanceBeforeTime(update.effectiveTime);
|
||||
checkState(oldBalance.isPresent(), "No balance found before effective time");
|
||||
Money actualOldBalance = oldBalance.get();
|
||||
Money expectedOldBalance = Money.ofMinor(credit.getCurrency(), update.oldBalance);
|
||||
checkState(actualOldBalance.equals(expectedOldBalance),
|
||||
"Real old balance does not match expected old balance (%s vs. %s)",
|
||||
actualOldBalance,
|
||||
expectedOldBalance);
|
||||
|
||||
// Create the new balance amount.
|
||||
Money newBalance = Money.ofMinor(credit.getCurrency(), update.newBalance);
|
||||
System.out.printf(" - %s -> %s\n", actualOldBalance, newBalance);
|
||||
|
||||
// Create and stage the new credit balance object for the new balance amount.
|
||||
RegistrarCreditBalance newCreditBalance = new RegistrarCreditBalance.Builder()
|
||||
.setParent(credit)
|
||||
.setEffectiveTime(update.effectiveTime)
|
||||
.setWrittenTime(now)
|
||||
.setAmount(newBalance)
|
||||
.build();
|
||||
stageEntityChange(null, newCreditBalance);
|
||||
}
|
||||
}
|
64
java/google/registry/tools/UpdateCursorsCommand.java
Normal file
64
java/google/registry/tools/UpdateCursorsCommand.java
Normal file
|
@ -0,0 +1,64 @@
|
|||
// 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;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.model.registry.RegistryCursor;
|
||||
import com.google.domain.registry.model.registry.RegistryCursor.CursorType;
|
||||
import com.google.domain.registry.tools.params.DateTimeParameter;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Modifies {code RegistryCursor} timestamps used by locking rolling cursor tasks, like in RDE. */
|
||||
@Parameters(separators = " =", commandDescription = "Modifies cursor timestamps used by LRC tasks")
|
||||
final class UpdateCursorsCommand extends MutatingCommand {
|
||||
|
||||
@Parameter(
|
||||
description = "TLDs on which to operate.",
|
||||
required = true)
|
||||
private List<String> tlds;
|
||||
|
||||
@Parameter(
|
||||
names = "--type",
|
||||
description = "Which cursor to update.",
|
||||
required = true)
|
||||
private CursorType cursorType;
|
||||
|
||||
@Parameter(
|
||||
names = "--timestamp",
|
||||
description = "The new timestamp to set.",
|
||||
validateWith = DateTimeParameter.class,
|
||||
required = true)
|
||||
private DateTime newTimestamp;
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
for (String tld : tlds) {
|
||||
Registry registry = Registry.get(tld);
|
||||
Optional<DateTime> expectedTimestamp = RegistryCursor.load(registry, cursorType);
|
||||
stageEntityChange(
|
||||
expectedTimestamp.isPresent()
|
||||
? RegistryCursor.create(registry, cursorType, expectedTimestamp.get())
|
||||
: null,
|
||||
RegistryCursor.create(registry, cursorType, newTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue