mirror of
https://github.com/google/nomulus.git
synced 2025-05-02 21:17:50 +02:00
Two commands are being added: - ImportDatastoreCommand starts an async import operation. User may choose to wait until import completes or quit immediately. - GetOperationStatusCommand checks the status of an operation. It may be used to check the status of an operation started by ImportDatastoreCommand. Both commands communicate with Datastore admin api directly, without going through the Registry server. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=228400059
277 lines
10 KiB
Java
277 lines
10 KiB
Java
// Copyright 2018 The Nomulus Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package google.registry.export.datastore;
|
|
|
|
import static com.google.common.base.Preconditions.checkArgument;
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
|
|
import com.google.api.client.googleapis.services.json.AbstractGoogleJsonClient;
|
|
import com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest;
|
|
import com.google.api.client.http.HttpRequestInitializer;
|
|
import com.google.api.client.http.HttpTransport;
|
|
import com.google.api.client.json.GenericJson;
|
|
import com.google.api.client.json.JsonFactory;
|
|
import com.google.api.client.util.Key;
|
|
import com.google.common.base.Strings;
|
|
import com.google.common.collect.ImmutableList;
|
|
import java.util.Collection;
|
|
import java.util.Optional;
|
|
|
|
/**
|
|
* Java client to <a href="https://cloud.google.com/datastore/docs/reference/admin/rest/">Cloud
|
|
* Datastore Admin REST API</a>.
|
|
*/
|
|
public class DatastoreAdmin extends AbstractGoogleJsonClient {
|
|
|
|
private static final String ROOT_URL = "https://datastore.googleapis.com/v1/";
|
|
private static final String SERVICE_PATH = "";
|
|
|
|
// GCP project that this instance is associated with.
|
|
private final String projectId;
|
|
|
|
protected DatastoreAdmin(Builder builder) {
|
|
super(builder);
|
|
this.projectId = checkNotNull(builder.projectId, "GCP projectId missing.");
|
|
}
|
|
|
|
/**
|
|
* Returns an {@link Export} request that starts exporting all Cloud Datastore databases owned by
|
|
* the GCP project identified by {@link #projectId}.
|
|
*
|
|
* <p>Typical usage is:
|
|
*
|
|
* <pre>
|
|
* {@code Export export = datastoreAdmin.export(parameters ...);}
|
|
* {@code Operation operation = export.execute();}
|
|
* {@code while (!operation.isSuccessful()) { ...}}
|
|
* </pre>
|
|
*
|
|
* <p>Please see the <a
|
|
* href="https://cloud.google.com/datastore/docs/reference/admin/rest/v1/projects/export">API
|
|
* specification of the export method for details</a>.
|
|
*
|
|
* <p>The following undocumented behaviors with regard to {@code outputUrlPrefix} have been
|
|
* observed:
|
|
*
|
|
* <ul>
|
|
* <li>If outputUrlPrefix refers to a GCS bucket, exported data will be nested deeper in the
|
|
* bucket with a timestamped path. This is useful when periodical backups are desired
|
|
* <li>If outputUrlPrefix is a already a nested path in a GCS bucket, exported data will be put
|
|
* under this path. This means that a nested path is not reusable, since the export process
|
|
* by default would not overwrite existing files.
|
|
* </ul>
|
|
*
|
|
* @param outputUrlPrefix the full resource URL of the external storage location
|
|
* @param kinds the datastore 'kinds' to be exported. If empty, all kinds will be exported
|
|
*/
|
|
public Export export(String outputUrlPrefix, Collection<String> kinds) {
|
|
return new Export(new ExportRequest(outputUrlPrefix, kinds));
|
|
}
|
|
|
|
/**
|
|
* Imports the entire backup specified by {@code backupUrl} back to Cloud Datastore.
|
|
*
|
|
* <p>A successful backup restores deleted entities and reverts updates to existing entities since
|
|
* the backup time. However, it does not affect newly added entities.
|
|
*/
|
|
public Import importBackup(String backupUrl) {
|
|
return new Import(new ImportRequest(backupUrl, ImmutableList.of()));
|
|
}
|
|
|
|
/**
|
|
* Imports the backup specified by {@code backupUrl} back to Cloud Datastore. Only entities whose
|
|
* types are included in {@code kinds} are imported.
|
|
*
|
|
* @see #importBackup(String)
|
|
*/
|
|
public Import importBackup(String backupUrl, Collection<String> kinds) {
|
|
return new Import(new ImportRequest(backupUrl, kinds));
|
|
}
|
|
|
|
/**
|
|
* Returns a {@link Get} request that retrieves the details of an export or import {@link
|
|
* Operation}.
|
|
*
|
|
* @param operationName name of the {@code Operation} as returned by an export or import request
|
|
*/
|
|
public Get get(String operationName) {
|
|
return new Get(operationName);
|
|
}
|
|
|
|
/**
|
|
* Returns a {@link ListOperations} request that retrieves all export or import {@link Operation
|
|
* operations} matching {@code filter}.
|
|
*
|
|
* <p>Sample usage: find all operations started after 2018-10-31 00:00:00 UTC and has stopped:
|
|
*
|
|
* <pre>
|
|
* {@code String filter = "metadata.common.startTime>\"2018-10-31T0:0:0Z\" AND done=true";}
|
|
* {@code List<Operation> operations = datastoreAdmin.list(filter);}
|
|
* </pre>
|
|
*
|
|
* <p>Please refer to {@link Operation} for how to reference operation properties.
|
|
*/
|
|
public ListOperations list(String filter) {
|
|
checkArgument(!Strings.isNullOrEmpty(filter), "Filter must not be null or empty.");
|
|
return new ListOperations(Optional.of(filter));
|
|
}
|
|
|
|
/**
|
|
* Returns a {@link ListOperations} request that retrieves all export or import {@link Operation *
|
|
* operations}.
|
|
*/
|
|
public ListOperations listAll() {
|
|
return new ListOperations(Optional.empty());
|
|
}
|
|
|
|
/** Builder for {@link DatastoreAdmin}. */
|
|
public static class Builder extends AbstractGoogleJsonClient.Builder {
|
|
|
|
private String projectId;
|
|
|
|
public Builder(
|
|
HttpTransport httpTransport,
|
|
JsonFactory jsonFactory,
|
|
HttpRequestInitializer httpRequestInitializer) {
|
|
super(httpTransport, jsonFactory, ROOT_URL, SERVICE_PATH, httpRequestInitializer, false);
|
|
}
|
|
|
|
@Override
|
|
public Builder setApplicationName(String applicationName) {
|
|
return (Builder) super.setApplicationName(applicationName);
|
|
}
|
|
|
|
/** Sets the GCP project ID of the Cloud Datastore databases being managed. */
|
|
public Builder setProjectId(String projectId) {
|
|
this.projectId = projectId;
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public DatastoreAdmin build() {
|
|
return new DatastoreAdmin(this);
|
|
}
|
|
}
|
|
|
|
/** A request to export Cloud Datastore databases. */
|
|
public class Export extends DatastoreAdminRequest<Operation> {
|
|
|
|
Export(ExportRequest exportRequest) {
|
|
super(
|
|
DatastoreAdmin.this,
|
|
"POST",
|
|
"projects/{projectId}:export",
|
|
exportRequest,
|
|
Operation.class);
|
|
set("projectId", projectId);
|
|
}
|
|
}
|
|
|
|
/** A request to restore an backup to a Cloud Datastore database. */
|
|
public class Import extends DatastoreAdminRequest<Operation> {
|
|
|
|
Import(ImportRequest importRequest) {
|
|
super(
|
|
DatastoreAdmin.this,
|
|
"POST",
|
|
"projects/{projectId}:import",
|
|
importRequest,
|
|
Operation.class);
|
|
set("projectId", projectId);
|
|
}
|
|
}
|
|
|
|
/** A request to retrieve details of an export or import operation. */
|
|
public class Get extends DatastoreAdminRequest<Operation> {
|
|
|
|
Get(String operationName) {
|
|
super(DatastoreAdmin.this, "GET", operationName, null, Operation.class);
|
|
}
|
|
}
|
|
|
|
/** A request to retrieve all export or import operations matching a given filter. */
|
|
public class ListOperations extends DatastoreAdminRequest<Operation.OperationList> {
|
|
|
|
ListOperations(Optional<String> filter) {
|
|
super(
|
|
DatastoreAdmin.this,
|
|
"GET",
|
|
"projects/{projectId}/operations",
|
|
null,
|
|
Operation.OperationList.class);
|
|
set("projectId", projectId);
|
|
filter.ifPresent(f -> set("filter", f));
|
|
}
|
|
}
|
|
|
|
/** Base class of all DatastoreAdmin requests. */
|
|
abstract static class DatastoreAdminRequest<T> extends AbstractGoogleJsonClientRequest<T> {
|
|
/**
|
|
* @param client Google JSON client
|
|
* @param requestMethod HTTP Method
|
|
* @param uriTemplate URI template for the path relative to the base URL. If it starts with a
|
|
* "/" the base path from the base URL will be stripped out. The URI template can also be a
|
|
* full URL. URI template expansion is done using {@link
|
|
* com.google.api.client.http.UriTemplate#expand(String, String, Object, boolean)}
|
|
* @param jsonContent POJO that can be serialized into JSON content or {@code null} for none
|
|
* @param responseClass response class to parse into
|
|
*/
|
|
protected DatastoreAdminRequest(
|
|
DatastoreAdmin client,
|
|
String requestMethod,
|
|
String uriTemplate,
|
|
Object jsonContent,
|
|
Class<T> responseClass) {
|
|
super(client, requestMethod, uriTemplate, jsonContent, responseClass);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Model object that describes the JSON content in an export request.
|
|
*
|
|
* <p>Please note that some properties defined in the API are excluded, e.g., {@code databaseId}
|
|
* (not supported by Cloud Datastore) and labels (not used by Domain Registry).
|
|
*/
|
|
@SuppressWarnings("unused")
|
|
static class ExportRequest extends GenericJson {
|
|
@Key private final String outputUrlPrefix;
|
|
@Key private final EntityFilter entityFilter;
|
|
|
|
ExportRequest(String outputUrlPrefix, Collection<String> kinds) {
|
|
checkNotNull(outputUrlPrefix, "outputUrlPrefix");
|
|
checkArgument(!kinds.isEmpty(), "kinds must not be empty");
|
|
this.outputUrlPrefix = outputUrlPrefix;
|
|
this.entityFilter = new EntityFilter(kinds);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Model object that describes the JSON content in an export request.
|
|
*
|
|
* <p>Please note that some properties defined in the API are excluded, e.g., {@code databaseId}
|
|
* (not supported by Cloud Datastore) and labels (not used by Domain Registry).
|
|
*/
|
|
@SuppressWarnings("unused")
|
|
static class ImportRequest extends GenericJson {
|
|
@Key private final String inputUrl;
|
|
@Key private final EntityFilter entityFilter;
|
|
|
|
ImportRequest(String inputUrl, Collection<String> kinds) {
|
|
checkNotNull(inputUrl, "outputUrlPrefix");
|
|
this.inputUrl = inputUrl;
|
|
this.entityFilter = new EntityFilter(kinds);
|
|
}
|
|
}
|
|
}
|