|
@@ -36,6 +36,7 @@ import com.google.common.base.Preconditions;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
import org.apache.hadoop.conf.Configuration;
|
|
import org.apache.hadoop.conf.Configuration;
|
|
import org.apache.hadoop.conf.Configured;
|
|
import org.apache.hadoop.conf.Configured;
|
|
import org.apache.hadoop.fs.FileStatus;
|
|
import org.apache.hadoop.fs.FileStatus;
|
|
@@ -43,15 +44,17 @@ import org.apache.hadoop.fs.FileSystem;
|
|
import org.apache.hadoop.fs.LocatedFileStatus;
|
|
import org.apache.hadoop.fs.LocatedFileStatus;
|
|
import org.apache.hadoop.fs.Path;
|
|
import org.apache.hadoop.fs.Path;
|
|
import org.apache.hadoop.fs.RemoteIterator;
|
|
import org.apache.hadoop.fs.RemoteIterator;
|
|
-import org.apache.hadoop.fs.s3a.Constants;
|
|
|
|
import org.apache.hadoop.fs.s3a.S3AFileStatus;
|
|
import org.apache.hadoop.fs.s3a.S3AFileStatus;
|
|
import org.apache.hadoop.fs.s3a.S3AFileSystem;
|
|
import org.apache.hadoop.fs.s3a.S3AFileSystem;
|
|
|
|
+import org.apache.hadoop.fs.s3a.S3AUtils;
|
|
import org.apache.hadoop.fs.shell.CommandFormat;
|
|
import org.apache.hadoop.fs.shell.CommandFormat;
|
|
|
|
+import org.apache.hadoop.util.ExitUtil;
|
|
import org.apache.hadoop.util.GenericOptionsParser;
|
|
import org.apache.hadoop.util.GenericOptionsParser;
|
|
import org.apache.hadoop.util.Tool;
|
|
import org.apache.hadoop.util.Tool;
|
|
import org.apache.hadoop.util.ToolRunner;
|
|
import org.apache.hadoop.util.ToolRunner;
|
|
|
|
|
|
import static org.apache.hadoop.fs.s3a.Constants.*;
|
|
import static org.apache.hadoop.fs.s3a.Constants.*;
|
|
|
|
+import static org.apache.hadoop.service.launcher.LauncherExitCodes.*;
|
|
|
|
|
|
/**
|
|
/**
|
|
* CLI to manage S3Guard Metadata Store.
|
|
* CLI to manage S3Guard Metadata Store.
|
|
@@ -74,40 +77,46 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
"\t" + Init.NAME + " - " + Init.PURPOSE + "\n" +
|
|
"\t" + Init.NAME + " - " + Init.PURPOSE + "\n" +
|
|
"\t" + Destroy.NAME + " - " + Destroy.PURPOSE + "\n" +
|
|
"\t" + Destroy.NAME + " - " + Destroy.PURPOSE + "\n" +
|
|
"\t" + Import.NAME + " - " + Import.PURPOSE + "\n" +
|
|
"\t" + Import.NAME + " - " + Import.PURPOSE + "\n" +
|
|
|
|
+ "\t" + BucketInfo.NAME + " - " + BucketInfo.PURPOSE + "\n" +
|
|
"\t" + Diff.NAME + " - " + Diff.PURPOSE + "\n" +
|
|
"\t" + Diff.NAME + " - " + Diff.PURPOSE + "\n" +
|
|
- "\t" + Prune.NAME + " - " + Prune.PURPOSE + "\n";
|
|
|
|
|
|
+ "\t" + Prune.NAME + " - " + Prune.PURPOSE + "\n" +
|
|
|
|
+ "\t" + SetCapacity.NAME + " - " +SetCapacity.PURPOSE + "\n";
|
|
private static final String DATA_IN_S3_IS_PRESERVED
|
|
private static final String DATA_IN_S3_IS_PRESERVED
|
|
- = "(all data in S3 is preserved";
|
|
|
|
|
|
+ = "(all data in S3 is preserved)";
|
|
|
|
|
|
abstract public String getUsage();
|
|
abstract public String getUsage();
|
|
|
|
|
|
// Exit codes
|
|
// Exit codes
|
|
- static final int SUCCESS = 0;
|
|
|
|
- static final int INVALID_ARGUMENT = 1;
|
|
|
|
- static final int ERROR = 99;
|
|
|
|
|
|
+ static final int SUCCESS = EXIT_SUCCESS;
|
|
|
|
+ static final int INVALID_ARGUMENT = EXIT_COMMAND_ARGUMENT_ERROR;
|
|
|
|
+ static final int E_USAGE = EXIT_USAGE;
|
|
|
|
+ static final int ERROR = EXIT_FAIL;
|
|
|
|
+ static final int E_BAD_STATE = EXIT_NOT_ACCEPTABLE;
|
|
|
|
+ static final int E_NOT_FOUND = EXIT_NOT_FOUND;
|
|
|
|
|
|
private S3AFileSystem filesystem;
|
|
private S3AFileSystem filesystem;
|
|
private MetadataStore store;
|
|
private MetadataStore store;
|
|
private final CommandFormat commandFormat;
|
|
private final CommandFormat commandFormat;
|
|
|
|
|
|
- private static final String META_FLAG = "meta";
|
|
|
|
- private static final String DAYS_FLAG = "days";
|
|
|
|
- private static final String HOURS_FLAG = "hours";
|
|
|
|
- private static final String MINUTES_FLAG = "minutes";
|
|
|
|
- private static final String SECONDS_FLAG = "seconds";
|
|
|
|
|
|
+ public static final String META_FLAG = "meta";
|
|
|
|
+ public static final String DAYS_FLAG = "days";
|
|
|
|
+ public static final String HOURS_FLAG = "hours";
|
|
|
|
+ public static final String MINUTES_FLAG = "minutes";
|
|
|
|
+ public static final String SECONDS_FLAG = "seconds";
|
|
|
|
|
|
- private static final String REGION_FLAG = "region";
|
|
|
|
- private static final String READ_FLAG = "read";
|
|
|
|
- private static final String WRITE_FLAG = "write";
|
|
|
|
|
|
+ public static final String REGION_FLAG = "region";
|
|
|
|
+ public static final String READ_FLAG = "read";
|
|
|
|
+ public static final String WRITE_FLAG = "write";
|
|
|
|
|
|
/**
|
|
/**
|
|
* Constructor a S3Guard tool with HDFS configuration.
|
|
* Constructor a S3Guard tool with HDFS configuration.
|
|
* @param conf Configuration.
|
|
* @param conf Configuration.
|
|
|
|
+ * @param opts any boolean options to support
|
|
*/
|
|
*/
|
|
- protected S3GuardTool(Configuration conf) {
|
|
|
|
|
|
+ protected S3GuardTool(Configuration conf, String...opts) {
|
|
super(conf);
|
|
super(conf);
|
|
|
|
|
|
- commandFormat = new CommandFormat(0, Integer.MAX_VALUE);
|
|
|
|
|
|
+ commandFormat = new CommandFormat(0, Integer.MAX_VALUE, opts);
|
|
// For metadata store URI
|
|
// For metadata store URI
|
|
commandFormat.addOptionWithValue(META_FLAG);
|
|
commandFormat.addOptionWithValue(META_FLAG);
|
|
// DDB region.
|
|
// DDB region.
|
|
@@ -126,10 +135,10 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
* {@link Destroy}.
|
|
* {@link Destroy}.
|
|
*
|
|
*
|
|
* @param paths remaining parameters from CLI.
|
|
* @param paths remaining parameters from CLI.
|
|
- * @return false for invalid parameters.
|
|
|
|
* @throws IOException on I/O errors.
|
|
* @throws IOException on I/O errors.
|
|
|
|
+ * @throws ExitUtil.ExitException on validation errors
|
|
*/
|
|
*/
|
|
- boolean parseDynamoDBRegion(List<String> paths) throws IOException {
|
|
|
|
|
|
+ void parseDynamoDBRegion(List<String> paths) throws IOException {
|
|
Configuration conf = getConf();
|
|
Configuration conf = getConf();
|
|
String fromCli = getCommandFormat().getOptValue(REGION_FLAG);
|
|
String fromCli = getCommandFormat().getOptValue(REGION_FLAG);
|
|
String fromConf = conf.get(S3GUARD_DDB_REGION_KEY);
|
|
String fromConf = conf.get(S3GUARD_DDB_REGION_KEY);
|
|
@@ -137,37 +146,34 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
|
|
|
|
if (fromCli != null) {
|
|
if (fromCli != null) {
|
|
if (fromCli.isEmpty()) {
|
|
if (fromCli.isEmpty()) {
|
|
- System.err.println("No region provided with -" + REGION_FLAG + " flag");
|
|
|
|
- return false;
|
|
|
|
|
|
+ throw invalidArgs("No region provided with -" + REGION_FLAG + " flag");
|
|
}
|
|
}
|
|
if (hasS3Path) {
|
|
if (hasS3Path) {
|
|
- System.err.println("Providing both an S3 path and the -" + REGION_FLAG
|
|
|
|
|
|
+ throw invalidArgs("Providing both an S3 path and the"
|
|
|
|
+ + " -" + REGION_FLAG
|
|
+ " flag is not supported. If you need to specify a different "
|
|
+ " flag is not supported. If you need to specify a different "
|
|
+ "region than the S3 bucket, configure " + S3GUARD_DDB_REGION_KEY);
|
|
+ "region than the S3 bucket, configure " + S3GUARD_DDB_REGION_KEY);
|
|
- return false;
|
|
|
|
}
|
|
}
|
|
conf.set(S3GUARD_DDB_REGION_KEY, fromCli);
|
|
conf.set(S3GUARD_DDB_REGION_KEY, fromCli);
|
|
- return true;
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
if (fromConf != null) {
|
|
if (fromConf != null) {
|
|
if (fromConf.isEmpty()) {
|
|
if (fromConf.isEmpty()) {
|
|
- System.err.printf("No region provided with config %s, %n",
|
|
|
|
|
|
+ throw invalidArgs("No region provided with config %s",
|
|
S3GUARD_DDB_REGION_KEY);
|
|
S3GUARD_DDB_REGION_KEY);
|
|
- return false;
|
|
|
|
}
|
|
}
|
|
- return true;
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
if (hasS3Path) {
|
|
if (hasS3Path) {
|
|
String s3Path = paths.get(0);
|
|
String s3Path = paths.get(0);
|
|
initS3AFileSystem(s3Path);
|
|
initS3AFileSystem(s3Path);
|
|
- return true;
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
- System.err.println("No region found from -" + REGION_FLAG + " flag, " +
|
|
|
|
|
|
+ throw invalidArgs("No region found from -" + REGION_FLAG + " flag, " +
|
|
"config, or S3 bucket");
|
|
"config, or S3 bucket");
|
|
- return false;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -189,7 +195,7 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
String metaURI = getCommandFormat().getOptValue(META_FLAG);
|
|
String metaURI = getCommandFormat().getOptValue(META_FLAG);
|
|
if (metaURI != null && !metaURI.isEmpty()) {
|
|
if (metaURI != null && !metaURI.isEmpty()) {
|
|
URI uri = URI.create(metaURI);
|
|
URI uri = URI.create(metaURI);
|
|
- LOG.info("create metadata store: {}", uri + " scheme: "
|
|
|
|
|
|
+ LOG.info("Create metadata store: {}", uri + " scheme: "
|
|
+ uri.getScheme());
|
|
+ uri.getScheme());
|
|
switch (uri.getScheme().toLowerCase(Locale.ENGLISH)) {
|
|
switch (uri.getScheme().toLowerCase(Locale.ENGLISH)) {
|
|
case "local":
|
|
case "local":
|
|
@@ -225,35 +231,48 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Initialize S3A FileSystem instance.
|
|
|
|
|
|
+ * Create and initialize a new S3A FileSystem instance.
|
|
|
|
+ * This instance is always created without S3Guard, so allowing
|
|
|
|
+ * a previously created metastore to be patched in.
|
|
|
|
+ *
|
|
|
|
+ * Note: this is a bit convoluted as it needs to also handle the situation
|
|
|
|
+ * of a per-bucket option in core-site.xml, which isn't easily overridden.
|
|
|
|
+ * The new config and the setting of the values before any
|
|
|
|
+ * {@code Configuration.get()} calls are critical.
|
|
*
|
|
*
|
|
* @param path s3a URI
|
|
* @param path s3a URI
|
|
- * @throws IOException
|
|
|
|
|
|
+ * @throws IOException failure to init filesystem
|
|
|
|
+ * @throws ExitUtil.ExitException if the FS is not an S3A FS
|
|
*/
|
|
*/
|
|
void initS3AFileSystem(String path) throws IOException {
|
|
void initS3AFileSystem(String path) throws IOException {
|
|
- URI uri;
|
|
|
|
- try {
|
|
|
|
- uri = new URI(path);
|
|
|
|
- } catch (URISyntaxException e) {
|
|
|
|
- throw new IOException(e);
|
|
|
|
- }
|
|
|
|
|
|
+ URI uri = toUri(path);
|
|
// Make sure that S3AFileSystem does not hold an actual MetadataStore
|
|
// Make sure that S3AFileSystem does not hold an actual MetadataStore
|
|
// implementation.
|
|
// implementation.
|
|
- Configuration conf = getConf();
|
|
|
|
- conf.setClass(S3_METADATA_STORE_IMPL, NullMetadataStore.class,
|
|
|
|
- MetadataStore.class);
|
|
|
|
- FileSystem fs = FileSystem.get(uri, getConf());
|
|
|
|
|
|
+ Configuration conf = new Configuration(getConf());
|
|
|
|
+ String nullStore = NullMetadataStore.class.getName();
|
|
|
|
+ conf.set(S3_METADATA_STORE_IMPL, nullStore);
|
|
|
|
+ String bucket = uri.getHost();
|
|
|
|
+ S3AUtils.setBucketOption(conf,
|
|
|
|
+ bucket,
|
|
|
|
+ S3_METADATA_STORE_IMPL, S3GUARD_METASTORE_NULL);
|
|
|
|
+ String updatedBucketOption = S3AUtils.getBucketOption(conf, bucket,
|
|
|
|
+ S3_METADATA_STORE_IMPL);
|
|
|
|
+ LOG.debug("updated bucket store option {}", updatedBucketOption);
|
|
|
|
+ Preconditions.checkState(S3GUARD_METASTORE_NULL.equals(updatedBucketOption),
|
|
|
|
+ "Expected bucket option to be %s but was %s",
|
|
|
|
+ S3GUARD_METASTORE_NULL, updatedBucketOption);
|
|
|
|
+
|
|
|
|
+ FileSystem fs = FileSystem.newInstance(uri, conf);
|
|
if (!(fs instanceof S3AFileSystem)) {
|
|
if (!(fs instanceof S3AFileSystem)) {
|
|
- throw new IOException(
|
|
|
|
- String.format("URI %s is not a S3A file system: %s", uri,
|
|
|
|
- fs.getClass().getName()));
|
|
|
|
|
|
+ throw invalidArgs("URI %s is not a S3A file system: %s",
|
|
|
|
+ uri, fs.getClass().getName());
|
|
}
|
|
}
|
|
filesystem = (S3AFileSystem) fs;
|
|
filesystem = (S3AFileSystem) fs;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Parse CLI arguments and returns the position arguments.
|
|
* Parse CLI arguments and returns the position arguments.
|
|
- * The options are stored in {@link #commandFormat}
|
|
|
|
|
|
+ * The options are stored in {@link #commandFormat}.
|
|
*
|
|
*
|
|
* @param args command line arguments.
|
|
* @param args command line arguments.
|
|
* @return the position arguments from CLI.
|
|
* @return the position arguments from CLI.
|
|
@@ -285,11 +304,32 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
return commandFormat;
|
|
return commandFormat;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
+ public final int run(String[] args) throws Exception {
|
|
|
|
+ return run(args, System.out);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Run the tool, capturing the output (if the tool supports that).
|
|
|
|
+ *
|
|
|
|
+ * As well as returning an exit code, the implementations can choose to
|
|
|
|
+ * throw an instance of {@link ExitUtil.ExitException} with their exit
|
|
|
|
+ * code set to the desired exit value. The exit code of auch an exception
|
|
|
|
+ * is used for the tool's exit code, and the stack trace only logged at
|
|
|
|
+ * debug.
|
|
|
|
+ * @param args argument list
|
|
|
|
+ * @param out output stream
|
|
|
|
+ * @return the exit code to return.
|
|
|
|
+ * @throws Exception on any failure
|
|
|
|
+ * @throws ExitUtil.ExitException for an alternative clean exit
|
|
|
|
+ */
|
|
|
|
+ public abstract int run(String[] args, PrintStream out) throws Exception;
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Create the metadata store.
|
|
* Create the metadata store.
|
|
*/
|
|
*/
|
|
static class Init extends S3GuardTool {
|
|
static class Init extends S3GuardTool {
|
|
- private static final String NAME = "init";
|
|
|
|
|
|
+ public static final String NAME = "init";
|
|
public static final String PURPOSE = "initialize metadata repository";
|
|
public static final String PURPOSE = "initialize metadata repository";
|
|
private static final String USAGE = NAME + " [OPTIONS] [s3a://BUCKET]\n" +
|
|
private static final String USAGE = NAME + " [OPTIONS] [s3a://BUCKET]\n" +
|
|
"\t" + PURPOSE + "\n\n" +
|
|
"\t" + PURPOSE + "\n\n" +
|
|
@@ -325,7 +365,7 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public int run(String[] args) throws IOException {
|
|
|
|
|
|
+ public int run(String[] args, PrintStream out) throws Exception {
|
|
List<String> paths = parseArgs(args);
|
|
List<String> paths = parseArgs(args);
|
|
|
|
|
|
String readCap = getCommandFormat().getOptValue(READ_FLAG);
|
|
String readCap = getCommandFormat().getOptValue(READ_FLAG);
|
|
@@ -340,20 +380,92 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
}
|
|
}
|
|
|
|
|
|
// Validate parameters.
|
|
// Validate parameters.
|
|
- if (!parseDynamoDBRegion(paths)) {
|
|
|
|
- System.err.println(USAGE);
|
|
|
|
- return INVALID_ARGUMENT;
|
|
|
|
|
|
+ try {
|
|
|
|
+ parseDynamoDBRegion(paths);
|
|
|
|
+ } catch (ExitUtil.ExitException e) {
|
|
|
|
+ errorln(USAGE);
|
|
|
|
+ throw e;
|
|
|
|
+ }
|
|
|
|
+ MetadataStore store = initMetadataStore(true);
|
|
|
|
+ printStoreDiagnostics(out, store);
|
|
|
|
+ return SUCCESS;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Change the capacity of the metadata store.
|
|
|
|
+ */
|
|
|
|
+ static class SetCapacity extends S3GuardTool {
|
|
|
|
+ public static final String NAME = "set-capacity";
|
|
|
|
+ public static final String PURPOSE = "Alter metadata store IO capacity";
|
|
|
|
+ private static final String USAGE = NAME + " [OPTIONS] [s3a://BUCKET]\n" +
|
|
|
|
+ "\t" + PURPOSE + "\n\n" +
|
|
|
|
+ "Common options:\n" +
|
|
|
|
+ " -" + META_FLAG + " URL - Metadata repository details " +
|
|
|
|
+ "(implementation-specific)\n" +
|
|
|
|
+ "\n" +
|
|
|
|
+ "Amazon DynamoDB-specific options:\n" +
|
|
|
|
+ " -" + READ_FLAG + " UNIT - Provisioned read throughput units\n" +
|
|
|
|
+ " -" + WRITE_FLAG + " UNIT - Provisioned write through put units\n" +
|
|
|
|
+ "\n" +
|
|
|
|
+ " URLs for Amazon DynamoDB are of the form dynamodb://TABLE_NAME.\n" +
|
|
|
|
+ " Specifying both the -" + REGION_FLAG + " option and an S3A path\n" +
|
|
|
|
+ " is not supported.";
|
|
|
|
+
|
|
|
|
+ SetCapacity(Configuration conf) {
|
|
|
|
+ super(conf);
|
|
|
|
+ // read capacity.
|
|
|
|
+ getCommandFormat().addOptionWithValue(READ_FLAG);
|
|
|
|
+ // write capacity.
|
|
|
|
+ getCommandFormat().addOptionWithValue(WRITE_FLAG);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ String getName() {
|
|
|
|
+ return NAME;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String getUsage() {
|
|
|
|
+ return USAGE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int run(String[] args, PrintStream out) throws Exception {
|
|
|
|
+ List<String> paths = parseArgs(args);
|
|
|
|
+ Map<String, String> options = new HashMap<>();
|
|
|
|
+
|
|
|
|
+ String readCap = getCommandFormat().getOptValue(READ_FLAG);
|
|
|
|
+ if (StringUtils.isNotEmpty(readCap)) {
|
|
|
|
+ S3GuardTool.println(out, "Read capacity set to %s", readCap);
|
|
|
|
+ options.put(S3GUARD_DDB_TABLE_CAPACITY_READ_KEY, readCap);
|
|
|
|
+ }
|
|
|
|
+ String writeCap = getCommandFormat().getOptValue(WRITE_FLAG);
|
|
|
|
+ if (StringUtils.isNotEmpty(writeCap)) {
|
|
|
|
+ S3GuardTool.println(out, "Write capacity set to %s", writeCap);
|
|
|
|
+ options.put(S3GUARD_DDB_TABLE_CAPACITY_WRITE_KEY, writeCap);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Validate parameters.
|
|
|
|
+ try {
|
|
|
|
+ parseDynamoDBRegion(paths);
|
|
|
|
+ } catch (ExitUtil.ExitException e) {
|
|
|
|
+ errorln(USAGE);
|
|
|
|
+ throw e;
|
|
}
|
|
}
|
|
- initMetadataStore(true);
|
|
|
|
|
|
+ MetadataStore store = initMetadataStore(false);
|
|
|
|
+ store.updateParameters(options);
|
|
|
|
+ printStoreDiagnostics(out, store);
|
|
return SUCCESS;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Destroy a metadata store.
|
|
* Destroy a metadata store.
|
|
*/
|
|
*/
|
|
static class Destroy extends S3GuardTool {
|
|
static class Destroy extends S3GuardTool {
|
|
- private static final String NAME = "destroy";
|
|
|
|
|
|
+ public static final String NAME = "destroy";
|
|
public static final String PURPOSE = "destroy Metadata Store data "
|
|
public static final String PURPOSE = "destroy Metadata Store data "
|
|
+ DATA_IN_S3_IS_PRESERVED;
|
|
+ DATA_IN_S3_IS_PRESERVED;
|
|
private static final String USAGE = NAME + " [OPTIONS] [s3a://BUCKET]\n" +
|
|
private static final String USAGE = NAME + " [OPTIONS] [s3a://BUCKET]\n" +
|
|
@@ -383,19 +495,21 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
return USAGE;
|
|
return USAGE;
|
|
}
|
|
}
|
|
|
|
|
|
- public int run(String[] args) throws IOException {
|
|
|
|
|
|
+ public int run(String[] args, PrintStream out) throws Exception {
|
|
List<String> paths = parseArgs(args);
|
|
List<String> paths = parseArgs(args);
|
|
- if (!parseDynamoDBRegion(paths)) {
|
|
|
|
- System.err.println(USAGE);
|
|
|
|
- return INVALID_ARGUMENT;
|
|
|
|
|
|
+ try {
|
|
|
|
+ parseDynamoDBRegion(paths);
|
|
|
|
+ } catch (ExitUtil.ExitException e) {
|
|
|
|
+ errorln(USAGE);
|
|
|
|
+ throw e;
|
|
}
|
|
}
|
|
|
|
|
|
try {
|
|
try {
|
|
initMetadataStore(false);
|
|
initMetadataStore(false);
|
|
} catch (FileNotFoundException e) {
|
|
} catch (FileNotFoundException e) {
|
|
// indication that the table was not found
|
|
// indication that the table was not found
|
|
|
|
+ println(out, "Metadata Store does not exist.");
|
|
LOG.debug("Failed to bind to store to be destroyed", e);
|
|
LOG.debug("Failed to bind to store to be destroyed", e);
|
|
- LOG.info("Metadata Store does not exist.");
|
|
|
|
return SUCCESS;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -403,7 +517,7 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
"Metadata Store is not initialized");
|
|
"Metadata Store is not initialized");
|
|
|
|
|
|
getStore().destroy();
|
|
getStore().destroy();
|
|
- LOG.info("Metadata store is deleted.");
|
|
|
|
|
|
+ println(out, "Metadata store is deleted.");
|
|
return SUCCESS;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -412,7 +526,7 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
* Import s3 metadata to the metadata store.
|
|
* Import s3 metadata to the metadata store.
|
|
*/
|
|
*/
|
|
static class Import extends S3GuardTool {
|
|
static class Import extends S3GuardTool {
|
|
- private static final String NAME = "import";
|
|
|
|
|
|
+ public static final String NAME = "import";
|
|
public static final String PURPOSE = "import metadata from existing S3 " +
|
|
public static final String PURPOSE = "import metadata from existing S3 " +
|
|
"data";
|
|
"data";
|
|
private static final String USAGE = NAME + " [OPTIONS] [s3a://BUCKET]\n" +
|
|
private static final String USAGE = NAME + " [OPTIONS] [s3a://BUCKET]\n" +
|
|
@@ -498,21 +612,16 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
- public int run(String[] args) throws IOException {
|
|
|
|
|
|
+ public int run(String[] args, PrintStream out) throws Exception {
|
|
List<String> paths = parseArgs(args);
|
|
List<String> paths = parseArgs(args);
|
|
if (paths.isEmpty()) {
|
|
if (paths.isEmpty()) {
|
|
- System.err.println(getUsage());
|
|
|
|
- return INVALID_ARGUMENT;
|
|
|
|
|
|
+ errorln(getUsage());
|
|
|
|
+ throw invalidArgs("no arguments");
|
|
}
|
|
}
|
|
String s3Path = paths.get(0);
|
|
String s3Path = paths.get(0);
|
|
initS3AFileSystem(s3Path);
|
|
initS3AFileSystem(s3Path);
|
|
|
|
|
|
- URI uri;
|
|
|
|
- try {
|
|
|
|
- uri = new URI(s3Path);
|
|
|
|
- } catch (URISyntaxException e) {
|
|
|
|
- throw new IOException(e);
|
|
|
|
- }
|
|
|
|
|
|
+ URI uri = toUri(s3Path);
|
|
String filePath = uri.getPath();
|
|
String filePath = uri.getPath();
|
|
if (filePath.isEmpty()) {
|
|
if (filePath.isEmpty()) {
|
|
// If they specify a naked S3 URI (e.g. s3a://bucket), we'll consider
|
|
// If they specify a naked S3 URI (e.g. s3a://bucket), we'll consider
|
|
@@ -522,7 +631,11 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
Path path = new Path(filePath);
|
|
Path path = new Path(filePath);
|
|
FileStatus status = getFilesystem().getFileStatus(path);
|
|
FileStatus status = getFilesystem().getFileStatus(path);
|
|
|
|
|
|
- initMetadataStore(false);
|
|
|
|
|
|
+ try {
|
|
|
|
+ initMetadataStore(false);
|
|
|
|
+ } catch (FileNotFoundException e) {
|
|
|
|
+ throw storeNotFound(e);
|
|
|
|
+ }
|
|
|
|
|
|
long items = 1;
|
|
long items = 1;
|
|
if (status.isFile()) {
|
|
if (status.isFile()) {
|
|
@@ -532,17 +645,18 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
items = importDir(status);
|
|
items = importDir(status);
|
|
}
|
|
}
|
|
|
|
|
|
- System.out.printf("Inserted %d items into Metadata Store%n", items);
|
|
|
|
|
|
+ println(out, "Inserted %d items into Metadata Store", items);
|
|
|
|
|
|
return SUCCESS;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Show diffs between the s3 and metadata store.
|
|
* Show diffs between the s3 and metadata store.
|
|
*/
|
|
*/
|
|
static class Diff extends S3GuardTool {
|
|
static class Diff extends S3GuardTool {
|
|
- private static final String NAME = "diff";
|
|
|
|
|
|
+ public static final String NAME = "diff";
|
|
public static final String PURPOSE = "report on delta between S3 and " +
|
|
public static final String PURPOSE = "report on delta between S3 and " +
|
|
"repository";
|
|
"repository";
|
|
private static final String USAGE = NAME + " [OPTIONS] s3a://BUCKET\n" +
|
|
private static final String USAGE = NAME + " [OPTIONS] s3a://BUCKET\n" +
|
|
@@ -625,10 +739,10 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
|
|
|
|
if (differ(msStatus, s3Status)) {
|
|
if (differ(msStatus, s3Status)) {
|
|
if (s3Status != null) {
|
|
if (s3Status != null) {
|
|
- out.printf("%s%s%s%n", S3_PREFIX, SEP, formatFileStatus(s3Status));
|
|
|
|
|
|
+ println(out, "%s%s%s", S3_PREFIX, SEP, formatFileStatus(s3Status));
|
|
}
|
|
}
|
|
if (msStatus != null) {
|
|
if (msStatus != null) {
|
|
- out.printf("%s%s%s%n", MS_PREFIX, SEP, formatFileStatus(msStatus));
|
|
|
|
|
|
+ println(out, "%s%s%s", MS_PREFIX, SEP, formatFileStatus(msStatus));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -705,6 +819,7 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
try {
|
|
try {
|
|
s3Status = getFilesystem().getFileStatus(qualified);
|
|
s3Status = getFilesystem().getFileStatus(qualified);
|
|
} catch (FileNotFoundException e) {
|
|
} catch (FileNotFoundException e) {
|
|
|
|
+ /* ignored */
|
|
}
|
|
}
|
|
PathMetadata meta = getStore().get(qualified);
|
|
PathMetadata meta = getStore().get(qualified);
|
|
FileStatus msStatus = (meta != null && !meta.isDeleted()) ?
|
|
FileStatus msStatus = (meta != null && !meta.isDeleted()) ?
|
|
@@ -717,18 +832,13 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
List<String> paths = parseArgs(args);
|
|
List<String> paths = parseArgs(args);
|
|
if (paths.isEmpty()) {
|
|
if (paths.isEmpty()) {
|
|
out.println(USAGE);
|
|
out.println(USAGE);
|
|
- return INVALID_ARGUMENT;
|
|
|
|
|
|
+ throw invalidArgs("no arguments");
|
|
}
|
|
}
|
|
String s3Path = paths.get(0);
|
|
String s3Path = paths.get(0);
|
|
initS3AFileSystem(s3Path);
|
|
initS3AFileSystem(s3Path);
|
|
- initMetadataStore(true);
|
|
|
|
|
|
+ initMetadataStore(false);
|
|
|
|
|
|
- URI uri;
|
|
|
|
- try {
|
|
|
|
- uri = new URI(s3Path);
|
|
|
|
- } catch (URISyntaxException e) {
|
|
|
|
- throw new IOException(e);
|
|
|
|
- }
|
|
|
|
|
|
+ URI uri = toUri(s3Path);
|
|
Path root;
|
|
Path root;
|
|
if (uri.getPath().isEmpty()) {
|
|
if (uri.getPath().isEmpty()) {
|
|
root = new Path("/");
|
|
root = new Path("/");
|
|
@@ -741,17 +851,13 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
return SUCCESS;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
|
- public int run(String[] args) throws IOException {
|
|
|
|
- return run(args, System.out);
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Prune metadata that has not been modified recently.
|
|
* Prune metadata that has not been modified recently.
|
|
*/
|
|
*/
|
|
static class Prune extends S3GuardTool {
|
|
static class Prune extends S3GuardTool {
|
|
- private static final String NAME = "prune";
|
|
|
|
|
|
+ public static final String NAME = "prune";
|
|
public static final String PURPOSE = "truncate older metadata from " +
|
|
public static final String PURPOSE = "truncate older metadata from " +
|
|
"repository "
|
|
"repository "
|
|
+ DATA_IN_S3_IS_PRESERVED;;
|
|
+ DATA_IN_S3_IS_PRESERVED;;
|
|
@@ -803,18 +909,19 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
return unit.toMillis(parsed);
|
|
return unit.toMillis(parsed);
|
|
}
|
|
}
|
|
|
|
|
|
- @VisibleForTesting
|
|
|
|
public int run(String[] args, PrintStream out) throws
|
|
public int run(String[] args, PrintStream out) throws
|
|
InterruptedException, IOException {
|
|
InterruptedException, IOException {
|
|
List<String> paths = parseArgs(args);
|
|
List<String> paths = parseArgs(args);
|
|
- if (!parseDynamoDBRegion(paths)) {
|
|
|
|
- System.err.println(USAGE);
|
|
|
|
- return INVALID_ARGUMENT;
|
|
|
|
|
|
+ try {
|
|
|
|
+ parseDynamoDBRegion(paths);
|
|
|
|
+ } catch (ExitUtil.ExitException e) {
|
|
|
|
+ errorln(USAGE);
|
|
|
|
+ throw e;
|
|
}
|
|
}
|
|
initMetadataStore(false);
|
|
initMetadataStore(false);
|
|
|
|
|
|
Configuration conf = getConf();
|
|
Configuration conf = getConf();
|
|
- long confDelta = conf.getLong(Constants.S3GUARD_CLI_PRUNE_AGE, 0);
|
|
|
|
|
|
+ long confDelta = conf.getLong(S3GUARD_CLI_PRUNE_AGE, 0);
|
|
|
|
|
|
long cliDelta = 0;
|
|
long cliDelta = 0;
|
|
cliDelta += getDeltaComponent(TimeUnit.DAYS, "days");
|
|
cliDelta += getDeltaComponent(TimeUnit.DAYS, "days");
|
|
@@ -823,8 +930,7 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
cliDelta += getDeltaComponent(TimeUnit.SECONDS, "seconds");
|
|
cliDelta += getDeltaComponent(TimeUnit.SECONDS, "seconds");
|
|
|
|
|
|
if (confDelta <= 0 && cliDelta <= 0) {
|
|
if (confDelta <= 0 && cliDelta <= 0) {
|
|
- System.err.println(
|
|
|
|
- "You must specify a positive age for metadata to prune.");
|
|
|
|
|
|
+ errorln("You must specify a positive age for metadata to prune.");
|
|
}
|
|
}
|
|
|
|
|
|
// A delta provided on the CLI overrides if one is configured
|
|
// A delta provided on the CLI overrides if one is configured
|
|
@@ -842,35 +948,235 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
return SUCCESS;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get info about a bucket and its S3Guard integration status.
|
|
|
|
+ */
|
|
|
|
+ static class BucketInfo extends S3GuardTool {
|
|
|
|
+ public static final String NAME = "bucket-info";
|
|
|
|
+ public static final String GUARDED_FLAG = "guarded";
|
|
|
|
+ public static final String UNGUARDED_FLAG = "unguarded";
|
|
|
|
+ public static final String AUTH_FLAG = "auth";
|
|
|
|
+ public static final String NONAUTH_FLAG = "nonauth";
|
|
|
|
+ public static final String ENCRYPTION_FLAG = "encryption";
|
|
|
|
+
|
|
|
|
+ public static final String PURPOSE = "provide/check S3Guard information"
|
|
|
|
+ + " about a specific bucket";
|
|
|
|
+ private static final String USAGE = NAME + " [OPTIONS] s3a://BUCKET\n"
|
|
|
|
+ + "\t" + PURPOSE + "\n\n"
|
|
|
|
+ + "Common options:\n"
|
|
|
|
+ + " -" + GUARDED_FLAG + " - Require S3Guard\n"
|
|
|
|
+ + " -" + ENCRYPTION_FLAG
|
|
|
|
+ + " -require {none, sse-s3, sse-kms} - Require encryption policy";
|
|
|
|
+
|
|
|
|
+ BucketInfo(Configuration conf) {
|
|
|
|
+ super(conf, GUARDED_FLAG, UNGUARDED_FLAG, AUTH_FLAG, NONAUTH_FLAG);
|
|
|
|
+ CommandFormat format = getCommandFormat();
|
|
|
|
+ format.addOptionWithValue(ENCRYPTION_FLAG);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ String getName() {
|
|
|
|
+ return NAME;
|
|
|
|
+ }
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
- public int run(String[] args) throws InterruptedException, IOException {
|
|
|
|
- return run(args, System.out);
|
|
|
|
|
|
+ public String getUsage() {
|
|
|
|
+ return USAGE;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ public int run(String[] args, PrintStream out)
|
|
|
|
+ throws InterruptedException, IOException {
|
|
|
|
+ List<String> paths = parseArgs(args);
|
|
|
|
+ if (paths.isEmpty()) {
|
|
|
|
+ errorln(getUsage());
|
|
|
|
+ throw invalidArgs("No bucket specified");
|
|
|
|
+ }
|
|
|
|
+ String s3Path = paths.get(0);
|
|
|
|
+ S3AFileSystem fs = (S3AFileSystem) FileSystem.newInstance(
|
|
|
|
+ toUri(s3Path), getConf());
|
|
|
|
+ setFilesystem(fs);
|
|
|
|
+ Configuration conf = fs.getConf();
|
|
|
|
+ URI fsUri = fs.getUri();
|
|
|
|
+ MetadataStore store = fs.getMetadataStore();
|
|
|
|
+ println(out, "Filesystem %s", fsUri);
|
|
|
|
+ println(out, "Location: %s", fs.getBucketLocation());
|
|
|
|
+ boolean usingS3Guard = !(store instanceof NullMetadataStore);
|
|
|
|
+ boolean authMode = false;
|
|
|
|
+ if (usingS3Guard) {
|
|
|
|
+ out.printf("Filesystem %s is using S3Guard with store %s%n",
|
|
|
|
+ fsUri, store.toString());
|
|
|
|
+ printOption(out, "Authoritative S3Guard",
|
|
|
|
+ METADATASTORE_AUTHORITATIVE, "false");
|
|
|
|
+ authMode = conf.getBoolean(METADATASTORE_AUTHORITATIVE, false);
|
|
|
|
+ printStoreDiagnostics(out, store);
|
|
|
|
+ } else {
|
|
|
|
+ println(out, "Filesystem %s is not using S3Guard", fsUri);
|
|
|
|
+ }
|
|
|
|
+ println(out, "%nS3A Client");
|
|
|
|
+
|
|
|
|
+ String endpoint = conf.getTrimmed(ENDPOINT, "");
|
|
|
|
+ println(out, "\tEndpoint: %s=%s",
|
|
|
|
+ ENDPOINT,
|
|
|
|
+ StringUtils.isNotEmpty(endpoint) ? endpoint : "(unset)");
|
|
|
|
+ String encryption =
|
|
|
|
+ printOption(out, "\tEncryption", SERVER_SIDE_ENCRYPTION_ALGORITHM,
|
|
|
|
+ "none");
|
|
|
|
+ printOption(out, "\tInput seek policy", INPUT_FADVISE, INPUT_FADV_NORMAL);
|
|
|
|
+
|
|
|
|
+ CommandFormat commands = getCommandFormat();
|
|
|
|
+ if (usingS3Guard) {
|
|
|
|
+ if (commands.getOpt(UNGUARDED_FLAG)) {
|
|
|
|
+ throw badState("S3Guard is enabled for %s", fsUri);
|
|
|
|
+ }
|
|
|
|
+ if (commands.getOpt(AUTH_FLAG) && !authMode) {
|
|
|
|
+ throw badState("S3Guard is enabled for %s,"
|
|
|
|
+ + " but not in authoritative mode", fsUri);
|
|
|
|
+ }
|
|
|
|
+ if (commands.getOpt(NONAUTH_FLAG) && authMode) {
|
|
|
|
+ throw badState("S3Guard is enabled in authoritative mode for %s",
|
|
|
|
+ fsUri);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if (commands.getOpt(GUARDED_FLAG)) {
|
|
|
|
+ throw badState("S3Guard is not enabled for %s", fsUri);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String desiredEncryption = getCommandFormat()
|
|
|
|
+ .getOptValue(ENCRYPTION_FLAG);
|
|
|
|
+ if (StringUtils.isNotEmpty(desiredEncryption)
|
|
|
|
+ && !desiredEncryption.equalsIgnoreCase(encryption)) {
|
|
|
|
+ throw badState("Bucket %s: required encryption is %s"
|
|
|
|
+ + " but actual encryption is %s",
|
|
|
|
+ fsUri, desiredEncryption, encryption);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ out.flush();
|
|
|
|
+ return SUCCESS;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private String printOption(PrintStream out,
|
|
|
|
+ String description, String key, String defVal) {
|
|
|
|
+ String t = getFilesystem().getConf().getTrimmed(key, defVal);
|
|
|
|
+ println(out, "%s: %s=%s", description, key, t);
|
|
|
|
+ return t;
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
private static S3GuardTool command;
|
|
private static S3GuardTool command;
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Convert a path to a URI, catching any {@code URISyntaxException}
|
|
|
|
+ * and converting to an invalid args exception.
|
|
|
|
+ * @param s3Path path to convert to a URI
|
|
|
|
+ * @return a URI of the path
|
|
|
|
+ * @throws ExitUtil.ExitException INVALID_ARGUMENT if the URI is invalid
|
|
|
|
+ */
|
|
|
|
+ protected static URI toUri(String s3Path) {
|
|
|
|
+ URI uri;
|
|
|
|
+ try {
|
|
|
|
+ uri = new URI(s3Path);
|
|
|
|
+ } catch (URISyntaxException e) {
|
|
|
|
+ throw invalidArgs("Not a valid fileystem path: %s", s3Path);
|
|
|
|
+ }
|
|
|
|
+ return uri;
|
|
|
|
+ }
|
|
|
|
+
|
|
private static void printHelp() {
|
|
private static void printHelp() {
|
|
if (command == null) {
|
|
if (command == null) {
|
|
- System.err.println("Usage: hadoop " + USAGE);
|
|
|
|
- System.err.println("\tperform S3Guard metadata store " +
|
|
|
|
|
|
+ errorln("Usage: hadoop " + USAGE);
|
|
|
|
+ errorln("\tperform S3Guard metadata store " +
|
|
"administrative commands.");
|
|
"administrative commands.");
|
|
} else {
|
|
} else {
|
|
- System.err.println("Usage: hadoop " + command.getUsage());
|
|
|
|
|
|
+ errorln("Usage: hadoop " + command.getUsage());
|
|
}
|
|
}
|
|
|
|
+ errorln();
|
|
|
|
+ errorln(COMMON_USAGE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static void errorln() {
|
|
System.err.println();
|
|
System.err.println();
|
|
- System.err.println(COMMON_USAGE);
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static void errorln(String x) {
|
|
|
|
+ System.err.println(x);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Print a formatted string followed by a newline to the output stream.
|
|
|
|
+ * @param out destination
|
|
|
|
+ * @param format format string
|
|
|
|
+ * @param args optional arguments
|
|
|
|
+ */
|
|
|
|
+ private static void println(PrintStream out, String format, Object... args) {
|
|
|
|
+ out.println(String.format(format, args));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Retrieve and Print store diagnostics.
|
|
|
|
+ * @param out output stream
|
|
|
|
+ * @param store store
|
|
|
|
+ * @throws IOException Failure to retrieve the data.
|
|
|
|
+ */
|
|
|
|
+ protected static void printStoreDiagnostics(PrintStream out,
|
|
|
|
+ MetadataStore store)
|
|
|
|
+ throws IOException {
|
|
|
|
+ Map<String, String> diagnostics = store.getDiagnostics();
|
|
|
|
+ out.println("Metadata Store Diagnostics:");
|
|
|
|
+ for (Map.Entry<String, String> entry : diagnostics.entrySet()) {
|
|
|
|
+ println(out, "\t%s=%s", entry.getKey(), entry.getValue());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Handle store not found by converting to an exit exception
|
|
|
|
+ * with specific error code.
|
|
|
|
+ * @param e exception
|
|
|
|
+ * @return a new exception to throw
|
|
|
|
+ */
|
|
|
|
+ protected static ExitUtil.ExitException storeNotFound(
|
|
|
|
+ FileNotFoundException e) {
|
|
|
|
+ return new ExitUtil.ExitException(
|
|
|
|
+ E_NOT_FOUND, e.toString(), e);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Build the exception to raise on invalid arguments.
|
|
|
|
+ * @param format string format
|
|
|
|
+ * @param args optional arguments for the string
|
|
|
|
+ * @return a new exception to throw
|
|
|
|
+ */
|
|
|
|
+ protected static ExitUtil.ExitException invalidArgs(
|
|
|
|
+ String format, Object...args) {
|
|
|
|
+ return new ExitUtil.ExitException(INVALID_ARGUMENT,
|
|
|
|
+ String.format(format, args));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Build the exception to raise on a bad store/bucket state.
|
|
|
|
+ * @param format string format
|
|
|
|
+ * @param args optional arguments for the string
|
|
|
|
+ * @return a new exception to throw
|
|
|
|
+ */
|
|
|
|
+ protected static ExitUtil.ExitException badState(
|
|
|
|
+ String format, Object...args) {
|
|
|
|
+ return new ExitUtil.ExitException(E_BAD_STATE,
|
|
|
|
+ String.format(format, args));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Execute the command with the given arguments.
|
|
* Execute the command with the given arguments.
|
|
*
|
|
*
|
|
- * @param args command specific arguments.
|
|
|
|
* @param conf Hadoop configuration.
|
|
* @param conf Hadoop configuration.
|
|
|
|
+ * @param args command specific arguments.
|
|
* @return exit code.
|
|
* @return exit code.
|
|
* @throws Exception on I/O errors.
|
|
* @throws Exception on I/O errors.
|
|
*/
|
|
*/
|
|
- public static int run(String[] args, Configuration conf) throws
|
|
|
|
|
|
+ public static int run(Configuration conf, String...args) throws
|
|
Exception {
|
|
Exception {
|
|
/* ToolRunner.run does this too, but we must do it before looking at
|
|
/* ToolRunner.run does this too, but we must do it before looking at
|
|
subCommand or instantiating the cmd object below */
|
|
subCommand or instantiating the cmd object below */
|
|
@@ -878,9 +1184,10 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
.getRemainingArgs();
|
|
.getRemainingArgs();
|
|
if (otherArgs.length == 0) {
|
|
if (otherArgs.length == 0) {
|
|
printHelp();
|
|
printHelp();
|
|
- return INVALID_ARGUMENT;
|
|
|
|
|
|
+ throw new ExitUtil.ExitException(E_USAGE, "No arguments provided");
|
|
}
|
|
}
|
|
final String subCommand = otherArgs[0];
|
|
final String subCommand = otherArgs[0];
|
|
|
|
+ LOG.debug("Executing command {}", subCommand);
|
|
switch (subCommand) {
|
|
switch (subCommand) {
|
|
case Init.NAME:
|
|
case Init.NAME:
|
|
command = new Init(conf);
|
|
command = new Init(conf);
|
|
@@ -891,15 +1198,22 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
case Import.NAME:
|
|
case Import.NAME:
|
|
command = new Import(conf);
|
|
command = new Import(conf);
|
|
break;
|
|
break;
|
|
|
|
+ case BucketInfo.NAME:
|
|
|
|
+ command = new BucketInfo(conf);
|
|
|
|
+ break;
|
|
case Diff.NAME:
|
|
case Diff.NAME:
|
|
command = new Diff(conf);
|
|
command = new Diff(conf);
|
|
break;
|
|
break;
|
|
case Prune.NAME:
|
|
case Prune.NAME:
|
|
command = new Prune(conf);
|
|
command = new Prune(conf);
|
|
break;
|
|
break;
|
|
|
|
+ case SetCapacity.NAME:
|
|
|
|
+ command = new SetCapacity(conf);
|
|
|
|
+ break;
|
|
default:
|
|
default:
|
|
printHelp();
|
|
printHelp();
|
|
- return INVALID_ARGUMENT;
|
|
|
|
|
|
+ throw new ExitUtil.ExitException(E_USAGE,
|
|
|
|
+ "Unknown command " + subCommand);
|
|
}
|
|
}
|
|
return ToolRunner.run(conf, command, otherArgs);
|
|
return ToolRunner.run(conf, command, otherArgs);
|
|
}
|
|
}
|
|
@@ -910,15 +1224,22 @@ public abstract class S3GuardTool extends Configured implements Tool {
|
|
*/
|
|
*/
|
|
public static void main(String[] args) {
|
|
public static void main(String[] args) {
|
|
try {
|
|
try {
|
|
- int ret = run(args, new Configuration());
|
|
|
|
- System.exit(ret);
|
|
|
|
|
|
+ int ret = run(new Configuration(), args);
|
|
|
|
+ exit(ret, "");
|
|
} catch (CommandFormat.UnknownOptionException e) {
|
|
} catch (CommandFormat.UnknownOptionException e) {
|
|
- System.err.println(e.getMessage());
|
|
|
|
|
|
+ errorln(e.getMessage());
|
|
printHelp();
|
|
printHelp();
|
|
- System.exit(INVALID_ARGUMENT);
|
|
|
|
|
|
+ exit(E_USAGE, e.getMessage());
|
|
|
|
+ } catch (ExitUtil.ExitException e) {
|
|
|
|
+ // explicitly raised exit code
|
|
|
|
+ exit(e.getExitCode(), e.toString());
|
|
} catch (Throwable e) {
|
|
} catch (Throwable e) {
|
|
e.printStackTrace(System.err);
|
|
e.printStackTrace(System.err);
|
|
- System.exit(ERROR);
|
|
|
|
|
|
+ exit(ERROR, e.toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ protected static void exit(int status, String text) {
|
|
|
|
+ ExitUtil.terminate(status, text);
|
|
|
|
+ }
|
|
}
|
|
}
|