HDDS-455. Ozone genconf tool must use picocli. Contributed by Dinesh Chitlangia.

This commit is contained in:
Hanisha Koneru 2018-09-28 13:06:50 -07:00
parent 19ad5be651
commit 35c7351f4b
3 changed files with 173 additions and 138 deletions

View File

@ -53,7 +53,7 @@ following command. This will generate a template called ```ozone-site.xml``` at
the specified path (directory). the specified path (directory).
{{< highlight bash >}} {{< highlight bash >}}
ozone genconf -output <path> ozone genconf <path>
{{< /highlight >}} {{< /highlight >}}
Let us look at the settings inside the generated file (ozone-site.xml) and Let us look at the settings inside the generated file (ozone-site.xml) and

View File

@ -18,7 +18,12 @@
package org.apache.hadoop.ozone.genconf; package org.apache.hadoop.ozone.genconf;
import org.apache.hadoop.hdds.cli.GenericCli;
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
import org.apache.hadoop.hdds.conf.OzoneConfiguration; import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.PicocliException;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
@ -30,112 +35,58 @@
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* GenerateOzoneRequiredConfigurations - A tool to generate ozone-site.xml<br> * GenerateOzoneRequiredConfigurations - A tool to generate ozone-site.xml<br>
* This tool generates an ozone-site.xml with minimally required configs. * This tool generates an ozone-site.xml with minimally required configs.
* This tool can be invoked as follows:<br> * This tool can be invoked as follows:<br>
* <ul> * <ul>
* <li>ozone genconf -output <Path to output file></li> * <li>ozone genconf <Path to output file></li>
* <li>ozone genconf -help</li> * <li>ozone genconf --help</li>
* <li>ozone genconf -h</li>
* </ul> * </ul>
*/ */
public final class GenerateOzoneRequiredConfigurations { @Command(
name = "ozone genconf",
description = "Tool to generate template ozone-site.xml",
versionProvider = HddsVersionProvider.class,
mixinStandardHelpOptions = true)
public final class GenerateOzoneRequiredConfigurations extends GenericCli {
private static final String OUTPUT = "-output"; @Parameters(arity = "1..1",
private static final String HELP = "-help"; description = "Directory path where ozone-site file should be generated.")
private static final String USAGE = "Usage: \nozone genconf " private String path;
+ OUTPUT + " <Path to output file> \n"
+ "ozone genconf "
+ HELP;
private static final int SUCCESS = 0;
private static final int FAILURE = 1;
private GenerateOzoneRequiredConfigurations() {
}
/** /**
* Entry point for using genconf tool. * Entry point for using genconf tool.
* *
* @param args * @param args
* @throws JAXBException *
*/ */
public static void main(String[] args) { public static void main(String[] args) throws Exception {
new GenerateOzoneRequiredConfigurations().run(args);
try {
if (args.length == 0) {
System.out.println(USAGE);
System.exit(1);
}
switch (args[0]) {
case OUTPUT:
if (args.length > 1) {
int result = generateConfigurations(args[1]);
} else {
System.out.println("Path to output file is mandatory");
System.out.println(USAGE);
System.exit(1);
}
break;
case HELP:
System.out.println(USAGE);
System.exit(0);
break;
default:
System.out.println(USAGE);
System.exit(1);
}
} catch (Exception e) {
e.printStackTrace();
}
} }
/** @Override
* Check if the path is valid directory. public Void call() throws Exception {
* generateConfigurations(path);
* @param path return null;
* @return true, if path is valid directory, else return false
*/
public static boolean isValidPath(String path) {
try {
return Files.isDirectory(Paths.get(path));
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
}
/**
* Check if user has permission to write in the specified path.
*
* @param path
* @return true, if the user has permission to write, else returns false
*/
public static boolean canWrite(String path) {
File file = new File(path);
return file.canWrite();
} }
/** /**
* Generate ozone-site.xml at specified path. * Generate ozone-site.xml at specified path.
*
* @param path * @param path
* @return SUCCESS(0) if file can be generated, else returns FAILURE(1) * @throws PicocliException
* @throws JAXBException * @throws JAXBException
*/ */
public static int generateConfigurations(String path) throws JAXBException { public static void generateConfigurations(String path) throws
PicocliException, JAXBException {
if (!isValidPath(path)) { if (!isValidPath(path)) {
System.out.println("Invalid directory path."); throw new PicocliException("Invalid directory path.");
return FAILURE;
} }
if (!canWrite(path)) { if (!canWrite(path)) {
System.out.println("Insufficient permission."); throw new PicocliException("Insufficient permission.");
return FAILURE;
} }
OzoneConfiguration oc = new OzoneConfiguration(); OzoneConfiguration oc = new OzoneConfiguration();
@ -168,7 +119,30 @@ public static int generateConfigurations(String path) throws JAXBException {
m.marshal(requiredConfig, new File(path, "ozone-site.xml")); m.marshal(requiredConfig, new File(path, "ozone-site.xml"));
System.out.println("ozone-site.xml has been generated at " + path); System.out.println("ozone-site.xml has been generated at " + path);
}
return SUCCESS; /**
* Check if the path is valid directory.
*
* @param path
* @return true, if path is valid directory, else return false
*/
public static boolean isValidPath(String path) {
try {
return Files.isDirectory(Paths.get(path));
} catch (InvalidPathException | NullPointerException ex) {
return Boolean.FALSE;
}
}
/**
* Check if user has permission to write in the specified path.
*
* @param path
* @return true, if the user has permission to write, else returns false
*/
public static boolean canWrite(String path) {
File file = new File(path);
return file.canWrite();
} }
} }

View File

@ -21,23 +21,40 @@
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.hadoop.test.GenericTestUtils; import org.apache.hadoop.test.GenericTestUtils;
import org.hamcrest.CoreMatchers; import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine;
import picocli.CommandLine.ExecutionException;
import picocli.CommandLine.IExceptionHandler2;
import picocli.CommandLine.ParseResult;
import picocli.CommandLine.ParameterException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** /**
* Tests GenerateOzoneRequiredConfigurations. * Tests GenerateOzoneRequiredConfigurations.
*/ */
public class TestGenerateOzoneRequiredConfigurations { public class TestGenerateOzoneRequiredConfigurations {
private static File outputBaseDir; private static File outputBaseDir;
private static GenerateOzoneRequiredConfigurations genconfTool;
private static final Logger LOG =
LoggerFactory.getLogger(TestGenerateOzoneRequiredConfigurations.class);
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
private final ByteArrayOutputStream err = new ByteArrayOutputStream();
private static final PrintStream OLD_OUT = System.out;
private static final PrintStream OLD_ERR = System.err;
/** /**
* Creates output directory which will be used by the test-cases. * Creates output directory which will be used by the test-cases.
* If a test-case needs a separate directory, it has to create a random * If a test-case needs a separate directory, it has to create a random
@ -49,6 +66,24 @@ public class TestGenerateOzoneRequiredConfigurations {
public static void init() throws Exception { public static void init() throws Exception {
outputBaseDir = GenericTestUtils.getTestDir(); outputBaseDir = GenericTestUtils.getTestDir();
FileUtils.forceMkdir(outputBaseDir); FileUtils.forceMkdir(outputBaseDir);
genconfTool = new GenerateOzoneRequiredConfigurations();
}
@Before
public void setup() throws Exception {
System.setOut(new PrintStream(out));
System.setErr(new PrintStream(err));
}
@After
public void reset() {
// reset stream after each unit test
out.reset();
err.reset();
// restore system streams
System.setOut(OLD_OUT);
System.setErr(OLD_ERR);
} }
/** /**
@ -59,6 +94,57 @@ public static void cleanup() throws IOException {
FileUtils.deleteDirectory(outputBaseDir); FileUtils.deleteDirectory(outputBaseDir);
} }
private void execute(String[] args, String msg) {
List<String> arguments = new ArrayList(Arrays.asList(args));
LOG.info("Executing shell command with args {}", arguments);
CommandLine cmd = genconfTool.getCmd();
IExceptionHandler2<List<Object>> exceptionHandler =
new IExceptionHandler2<List<Object>>() {
@Override
public List<Object> handleParseException(ParameterException ex,
String[] args) {
throw ex;
}
@Override
public List<Object> handleExecutionException(ExecutionException ex,
ParseResult parseResult) {
throw ex;
}
};
cmd.parseWithHandlers(new CommandLine.RunLast(),
exceptionHandler, args);
Assert.assertTrue(out.toString().contains(msg));
}
private void executeWithException(String[] args, String msg) {
List<String> arguments = new ArrayList(Arrays.asList(args));
LOG.info("Executing shell command with args {}", arguments);
CommandLine cmd = genconfTool.getCmd();
IExceptionHandler2<List<Object>> exceptionHandler =
new IExceptionHandler2<List<Object>>() {
@Override
public List<Object> handleParseException(ParameterException ex,
String[] args) {
throw ex;
}
@Override
public List<Object> handleExecutionException(ExecutionException ex,
ParseResult parseResult) {
throw ex;
}
};
try{
cmd.parseWithHandlers(new CommandLine.RunLast(),
exceptionHandler, args);
}catch(Exception ex){
Assert.assertTrue(ex.getMessage().contains(msg));
}
}
/** /**
* Tests a valid path and generates ozone-site.xml by calling * Tests a valid path and generates ozone-site.xml by calling
* {@code GenerateOzoneRequiredConfigurations#generateConfigurations}. * {@code GenerateOzoneRequiredConfigurations#generateConfigurations}.
@ -68,79 +154,54 @@ public static void cleanup() throws IOException {
@Test @Test
public void testGenerateConfigurations() throws Exception { public void testGenerateConfigurations() throws Exception {
File tempPath = getRandomTempDir(); File tempPath = getRandomTempDir();
String[] args = new String[]{"-output", tempPath.getAbsolutePath()}; String[] args = new String[]{tempPath.getAbsolutePath()};
execute(args, "ozone-site.xml has been generated at " +
Assert.assertEquals("Path is valid", tempPath.getAbsolutePath());
true, GenerateOzoneRequiredConfigurations.isValidPath(args[1]));
Assert.assertEquals("Permission is valid",
true, GenerateOzoneRequiredConfigurations.canWrite(args[1]));
Assert.assertEquals("Config file generated",
0, GenerateOzoneRequiredConfigurations.generateConfigurations(args[1]));
} }
/** /**
* Tests ozone-site.xml generation by calling * Test to avoid generating ozone-site.xml when insufficient permission.
* {@code GenerateOzoneRequiredConfigurations#main}.
*
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testGenerateConfigurationsThroughMainMethod() throws Exception { public void genconfFailureByInsufficientPermissions() throws Exception {
File tempPath = getRandomTempDir();
String[] args = new String[]{"-output", tempPath.getAbsolutePath()};
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
PrintStream oldStream = System.out;
try (PrintStream ps = new PrintStream(outContent)) {
System.setOut(ps);
GenerateOzoneRequiredConfigurations.main(args);
Assert.assertThat(outContent.toString(), CoreMatchers.containsString(
"ozone-site.xml has been generated at"));
System.setOut(oldStream);
}
}
/**
* Test to avoid generating ozone-site.xml when invalid permission.
* @throws Exception
*/
@Test
public void generateConfigurationsFailure() throws Exception {
File tempPath = getRandomTempDir(); File tempPath = getRandomTempDir();
tempPath.setReadOnly(); tempPath.setReadOnly();
String[] args = new String[]{"-output", tempPath.getAbsolutePath()}; String[] args = new String[]{tempPath.getAbsolutePath()};
GenerateOzoneRequiredConfigurations.main(args); executeWithException(args, "Insufficient permission.");
Assert.assertEquals("Path is valid",
true, GenerateOzoneRequiredConfigurations.isValidPath(args[1]));
Assert.assertEquals("Invalid permission",
false, GenerateOzoneRequiredConfigurations.canWrite(args[1]));
Assert.assertEquals("Config file not generated",
1, GenerateOzoneRequiredConfigurations.generateConfigurations(args[1]));
tempPath.setWritable(true);
} }
/** /**
* Test to avoid generating ozone-site.xml when invalid permission. * Test to avoid generating ozone-site.xml when invalid path.
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void generateConfigurationsFailureForInvalidPath() throws Exception { public void genconfFailureByInvalidPath() throws Exception {
File tempPath = getRandomTempDir(); File tempPath = getRandomTempDir();
tempPath.setReadOnly(); String[] args = new String[]{"invalid-path"};
String[] args = new String[]{"-output", executeWithException(args, "Invalid directory path.");
tempPath.getAbsolutePath() + "/ozone-site.xml"}; }
GenerateOzoneRequiredConfigurations.main(args);
Assert.assertEquals("Path is invalid", false, /**
GenerateOzoneRequiredConfigurations.isValidPath(args[1])); * Test to avoid generating ozone-site.xml when path not specified.
* @throws Exception
*/
@Test
public void genconfPathNotSpecified() throws Exception {
File tempPath = getRandomTempDir();
String[] args = new String[]{};
executeWithException(args, "Missing required parameter: <path>");
}
Assert.assertEquals("Config file not generated", 1, /**
GenerateOzoneRequiredConfigurations.generateConfigurations(args[1])); * Test to check help message.
tempPath.setWritable(true); * @throws Exception
*/
@Test
public void genconfHelp() throws Exception {
File tempPath = getRandomTempDir();
String[] args = new String[]{"--help"};
execute(args, "Usage: ozone genconf [-hV] [--verbose]");
} }
private File getRandomTempDir() throws IOException { private File getRandomTempDir() throws IOException {