Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI refactoring #650

Merged
merged 31 commits into from
Oct 3, 2018
Merged

CLI refactoring #650

merged 31 commits into from
Oct 3, 2018

Conversation

AlexandraRoatis
Copy link
Contributor

@AlexandraRoatis AlexandraRoatis commented Sep 25, 2018

Description

  1. Using the picocli library for parsing CLI input parameters.
  2. Support for absolute path use for log and database folders unable to set absolute db path #627.
  3. Correct and consistent behavior for the --network and --datadir options Add --network and --datadir options #524.
  4. Advanced Network enum with support for the mastery test net and custom networks.
  5. Compatibility with old kernels when the above options are not used and the config.xml and genesis.json files are encountered at the old location.
  6. Extensive unit tests for the new functionality.

Type of change

Insert x into the following checkboxes to confirm (eg. [x]):

  • Bug fix.
  • New feature.
  • Enhancement.
  • Unit test.
  • Breaking change (a fix or feature that causes existing functionality to not work as expected).
  • Requires documentation update.

Testing

Please describe the tests you used to validate this pull request. Provide any relevant details for test configurations as well as any instructions to reproduce these results.

  • Existing and newly added unit tests.
  • The following manual commands:
# saving initial config (withut ids)
rsync -rv config/ iniconfig/

###### will exit the kernel

### help tests

./aion.sh -n testnet -d data -other
./aion.sh -other
./aion.sh -h

### version tests

./aion.sh -v
./aion.sh --version

### config tests

# compatible with old kernels
rsync -rv iniconfig/mainnet/ config/ #; grep "id" config/config.xml
./aion.sh -c # writes to config/config.xml
grep "id" config/config.xml

# for new kernels
rm -fr config/config.xml config/genesis.json
rm -fr mainnet

rsync -rv iniconfig/mainnet/ config/mainnet/  #; grep "id" config/mainnet/config.xml
./aion.sh -c # writes to config/mainnet/config.xml
grep "id" config/mainnet/config.xml

rsync -rv iniconfig/mainnet/ config/mainnet/ #; grep "id" config/mainnet/config.xml
./aion.sh -c mainnet # writes to config/mainnet/config.xml
grep "id" config/mainnet/config.xml

rsync -rv iniconfig/mainnet/ config/mainnet/ #; grep "id" config/mainnet/config.xml
./aion.sh -c incorrect # writes to config/mainnet/config.xml
grep "id" config/mainnet/config.xml

rm -fr mastery

rsync -rv iniconfig/mastery/ config/mastery/ #; grep "id" config/mastery/config.xml
./aion.sh -c mastery # writes to config/mastery/config.xml
grep "id" config/mastery/config.xml

rsync -rv iniconfig/mastery/ config/mastery/ #; grep "id" config/mastery/config.xml
./aion.sh -c testnet # writes to config/mastery/config.xml
grep "id" config/mastery/config.xml

mkdir mainnet ; mkdir mainnet/config ; rsync -rv iniconfig/mainnet/ mainnet/config/
./aion.sh -c # writes to mainnet/config/config.xml
grep "id" mainnet/config/config.xml
./aion.sh -c mainnet # writes to mainnet/config/config.xml
grep "id" mainnet/config/config.xml
./aion.sh -c incorrect # writes to mainnet/config/config.xml
grep "id" mainnet/config/config.xml

### info tests

# compatible with old kernels
rsync -rv iniconfig/mainnet/ config/
./aion.sh -i # reads config/config.xml

# for new kernels
rm -fr config/config.xml config/genesis.json
rm -fr mainnet

rsync -rv iniconfig/mainnet/ config/mainnet/
./aion.sh -i # reads config/mainnet/config.xml
mkdir mainnet ; mkdir mainnet/config ; rsync -rv iniconfig/mainnet/ mainnet/config/
./aion.sh -i # reads mainnet/config/config.xml

rm -fr mainnet
./aion.sh -i -n mainnet # reads config/mainnet/config.xml
mkdir mainnet ; mkdir mainnet/config ; rsync -rv iniconfig/mainnet/ mainnet/config/
./aion.sh -i -n mainnet # reads mainnet/config/config.xml

rm -fr data/mastery
./aion.sh -i -n testnet -d data # reads config/mastery/config.xml
mkdir data; mkdir data/mastery ; mkdir data/mastery/config ; rsync -rv iniconfig/mainnet/ data/mastery/config/
./aion.sh -i -n testnet -d data # reads data/mastery/config/config.xml

###### will run the kernel

rsync -rv iniconfig/mainnet/ config/
./aion.sh # reads from config/config.xml

rm -fr mainnet config; rsync -rv  iniconfig/ config/
./aion.sh # reads from config/mainnet/config.xml
# do not delete data for next test

./aion.sh # reads from mainnet/config/config.xml

rm -fr data config; rsync -rv  iniconfig/ config/
./aion.sh -n testnet -d data # reads from config/mastery/config.xml
# do not delete data for next test

./aion.sh -n testnet -d data # reads from data/mastery/config/config.xml

###### using data from last execution
./aion.sh -n testnet -d data --dump-blocks

./aion.sh -n testnet -d data -r 100

./aion.sh -n testnet -d data --state TOP

./aion.sh ac -n testnet -d data # store account in data/mastery/keystore

./aion.sh --dump-state -n testnet -d data

Verification

Insert x into the following checkboxes to confirm (eg. [x]):

  • I have self-reviewed my own code and conformed to the style guidelines of this project.
  • New and existing tests pass locally with my changes.
  • I have added tests for my fix or feature.
  • I have made appropriate changes to the corresponding documentation.
  • My code generates no new warnings.
  • Any dependent changes have been made.

@AlexandraRoatis AlexandraRoatis added enhancement New feature or request wip Indicates that a PR or issue is Work In Progress, issuer should be notified before any actions feature labels Sep 25, 2018
@AlexandraRoatis AlexandraRoatis added this to the 0.3.2 milestone Sep 25, 2018
@AlexandraRoatis AlexandraRoatis self-assigned this Sep 25, 2018
@AlexandraRoatis AlexandraRoatis force-pushed the cli-parameters branch 12 times, most recently from 32ab904 to de09613 Compare September 27, 2018 14:29
Copy link
Contributor

@aion-kelvin aion-kelvin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Do we need to check that the given arguments don't clash? i.e. from looking at the code, I think doing something like ./aion.sh al ac will get past the check for argument parsing, but after it runs the code for create account, it'll just exit before knowing that list accounts was also requested. I think ideally, it should print out the help page and say that you can't do both. Or you could support both, but that would open a complicated can of worms

List<String> list = new ArrayList<>();

int i = 0;
while (i < arguments.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional: for loop seemed more intuitive to me...

}
// reading from correct config file
File configFile = cfg.getExecConfigFile();
if (!configFile.exists()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the given config file doesn't exist, can we either:

  1. exit with an error
  2. use the default but also print out a warning that the given input was not valid so we used the initial config (also, is it possible that the default config doesn't exist either?)

I feel like the user could get confused is he/she inputs something invalid and the program silently ignores it and does its own thing

Copy link
Contributor Author

@AlexandraRoatis AlexandraRoatis Sep 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will explain the reasoning behind that code in the comments below.
First, keep in mind the following 3 points:

  1. there exists a base directory where all the code and the aion.sh reside;
  2. we allow setting an execution path with the --datadir option and/or the --network option;
  3. we must maintain compatibility with old kernel executables.

So, in order to make these features compatible, I've opted for the following:

  1. If I find a config.xml and genesis.json at the old location, I will attempt to execute the CLI command or run the kernel as previous kernels would, in the base directory.
  2. But, if the user calls the kernel with either the --datadir value, --network value or --config value option, then it will take precedence over old executable style and behave in the following way:
    • we create an execution path which will be either a directory in the base path with the network name (to ensure that if I run different networks from the same base path, they will not clash with eachother) or the directory given in the datadir option plus the network;
    • we copy the configuration files to the execution path to save the configuration;
    • we create the keystore, log and database directories, if they do not already exist.

Copy link
Contributor Author

@AlexandraRoatis AlexandraRoatis Sep 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this setup, there are in fact 3 places from where a kernel can read it's configuration:

  1. config/config.xml like old kernels
  2. config/network/config.xml for new kernels with new execution paths
  3. network/config/config.xml for new kernels where the execution path already exists

So, in the case of new kernels I opted for checking 3. and if it doesn't exist going for 2.
This way I can continue an execution I already started as it was set up, but also start from scratch on new paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this will need to be well documented when we introduce the --datadir and --network options.

@AlexandraRoatis AlexandraRoatis force-pushed the cli-parameters branch 2 times, most recently from f94ed83 to 3f79bbb Compare September 27, 2018 20:12
Copy link
Contributor

@aion-kelvin aion-kelvin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey thanks for working on this. The extensive testing looks great. Have some thoughts below (in addition to my earlier one which I accidentally submitted before I was done)

*/
private boolean createAccount() {
String password = null, password2 = null;
String password, password2;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you didn't change this part, but while touching this code, would you mind changing that try ( .. ) to:

try( InputStreamReader isr = new InputStreamReader(System.in);
      BufferedReader reader = new BufferedReader(isr)) 

Otherwise try-with-resources doesn't know that InputStreamReader also needs to be auto-closed

return false;
}

String password = null, password2 = null;
String password, password2;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you didn't change this part, but while touching this code, would you mind changing that try ( .. ) to:

try( InputStreamReader isr = new InputStreamReader(System.in);
      BufferedReader reader = new BufferedReader(isr)) 

Otherwise try-with-resources doesn't know that InputStreamReader also needs to be auto-closed

* @param identifier a positive integer value representing the network identifier
* @return a custom network object with the given identifier.
*/
public static Object getCustomNet(int identifier) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why Object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug


// base paths
private static final String BASE_PATH = System.getProperty("user.dir");
private static final File MAIN_BASE_PATH = new File(BASE_PATH, "mainnet");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also use JUnit's TemporaryFolder to generate a temporary folder and set your base paths to that folder. Then you won't need to manually delete stuff in your @After

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created an issue for this refactoring #660

}

// Methods below taken from FileUtils class
private static boolean copyRecursively(File src, File target) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably look into using Apache Commons IO (or something similar) for these functions instead of just copy pasting them everywhere...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created an issue for this refactoring #660

modBoot/src/org/aion/Aion.java Show resolved Hide resolved
@@ -136,11 +129,242 @@ public void setConsensus(CfgConsensus _consensus) {
this.consensus = _consensus;
}

/* ------------ execution path management ------------ */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supporting the old directory structure as well as the new one is definitely convenient and I'm sure users will appreciate it. Is this something we should continue to support into the future though?

I think at some point (not in this PR) we should make a converter that converts the old structure to the new one so we don't have the burden of dealing with the two code paths. The transition could go like this:

  1. Make the script and release it
  2. Add message in kernel so if it detects old structure, print a message saying they need to migrate it using the script at some point, because we'll stop supporting it
  3. Wait a while, deprecate the code that supports it

Copy link
Contributor

@qoire qoire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@arajasek arajasek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@AlexandraRoatis
Copy link
Contributor Author

AlexandraRoatis commented Oct 2, 2018

I confirmed that the full list of commands in the PR description works correctly after the bug fixes in 47b2266.

@AlexandraRoatis
Copy link
Contributor Author

Reviewers are encouraged to check the commands as well and perform additional tests, as they see fit ;)

@AlexandraRoatis AlexandraRoatis removed the wip Indicates that a PR or issue is Work In Progress, issuer should be notified before any actions label Oct 2, 2018
@AlexandraRoatis
Copy link
Contributor Author

@aion-kelvin regarding the ./aion.sh al ac issue we will document the behavior and bounty out the incompatibility checks

@AionJayT AionJayT merged commit fd5721f into master-pre-merge Oct 3, 2018
@AlexandraRoatis AlexandraRoatis deleted the cli-parameters branch October 4, 2018 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants