// Copyright 2019 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import google.registry.export.datastore.DatastoreAdmin;
import google.registry.export.datastore.Operation;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.joda.time.Duration;
/**
* Command that imports an earlier backup into Datastore.
*
*
This command is part of the Datastore restore process. Please refer to the playbook for
* the entire process.
*/
@Parameters(separators = " =", commandDescription = "Imports a backup of the Datastore.")
public class ImportDatastoreCommand extends ConfirmingCommand {
@Parameter(names = "--backup_url", description = "URL to the backup on GCS to be imported.")
private String backupUrl;
@Nullable
@Parameter(
names = "--kinds",
description = "List of entity kinds to be imported. Default is to import all.")
private List kinds = ImmutableList.of();
@Parameter(
names = "--async",
description = "If true, command will launch import operation and quit.")
private boolean async;
@Parameter(
names = "--poll_interval",
description =
"Polling interval while waiting for completion synchronously. "
+ "Value is in ISO-8601 format, e.g., PT10S for 10 seconds.")
private Duration pollingInterval = Duration.standardSeconds(30);
@Parameter(
names = "--confirm_production_import",
description = "Set this option to 'PRODUCTION' to confirm import in production environment.")
private String confirmProductionImport = "";
@Inject DatastoreAdmin datastoreAdmin;
@Override
protected String execute() throws Exception {
RegistryToolEnvironment currentEnvironment = RegistryToolEnvironment.get();
// Extra confirmation for running in production
checkArgument(
!currentEnvironment.equals(RegistryToolEnvironment.PRODUCTION)
|| confirmProductionImport.equals("PRODUCTION"),
"The confirm_production_import option must be set when restoring production environment.");
Operation importOperation = datastoreAdmin.importBackup(backupUrl, kinds).execute();
String statusCommand =
String.format(
"nomulus -e %s get_operation_status %s",
Ascii.toLowerCase(currentEnvironment.name()), importOperation.getName());
if (async) {
return String.format(
"Datastore import started. Run this command to check its progress:\n%s",
statusCommand);
}
System.out.println(
"Waiting for import to complete.\n"
+ "You may press Ctrl-C at any time, and use this command to check progress:\n"
+ statusCommand);
while (importOperation.isProcessing()) {
waitInteractively(pollingInterval);
importOperation = datastoreAdmin.get(importOperation.getName()).execute();
System.out.printf("\n%s\n", importOperation.getProgress());
}
return String.format(
"\nDatastore import %s %s.",
importOperation.getName(), importOperation.isSuccessful() ? "succeeded" : "failed");
}
@Override
protected String prompt() {
return "\nThis command is an intermediate step in the Datastore restore process.\n\n"
+ "Please read and understand the playbook entry at\n"
+ " http://playbooks/domain_registry/procedures/backup-restore-testing.md\n"
+ "before proceeding.\n";
}
/** Prints dots to console at regular interval while waiting. */
private static void waitInteractively(Duration pollingInterval) throws InterruptedException {
int sleepSeconds = 2;
long iterations = (pollingInterval.getStandardSeconds() + sleepSeconds - 1) / sleepSeconds;
for (int i = 0; i < iterations; i++) {
TimeUnit.SECONDS.sleep(sleepSeconds);
System.out.print('.');
System.out.flush();
}
}
}