Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion exonum-java-binding/app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
~ 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.
~ limitations under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
Expand Down
136 changes: 131 additions & 5 deletions exonum-java-binding/core/rust/src/runtime/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,25 @@
* limitations under the License.
*/

use super::{paths::executable_directory, Config, JvmConfig, RuntimeConfig};
use exonum_cli::command::{
finalize::Finalize, generate_config::GenerateConfig, generate_template::GenerateTemplate,
maintenance::Maintenance, run::Run as StandardRun, ExonumCommand, StandardResult,
finalize::Finalize,
generate_config::{GenerateConfig, PUB_CONFIG_FILE_NAME, SEC_CONFIG_FILE_NAME},
generate_template::GenerateTemplate,
maintenance::Maintenance,
run::Run as StandardRun,
ExonumCommand, StandardResult,
};
use failure;
use serde::{Deserialize, Serialize};
use structopt::StructOpt;

use std::path::PathBuf;
use std::{path::PathBuf, str::FromStr};

use super::{paths::executable_directory, Config, JvmConfig, RuntimeConfig};

/// Exonum Java Bindings Application.
///
/// Configures and runs Exonum node with Java runtime enabled.
// TODO: support run-dev (ECR-3727)
#[derive(StructOpt, Debug)]
#[structopt(author, about)]
#[allow(clippy::large_enum_variant)]
Expand All @@ -46,6 +50,12 @@ pub enum Command {
/// Run the node with provided node config and Java runtime enabled.
#[structopt(name = "run")]
Run(Run),
/// Run the node in development mode (generate configuration and db files automatically).
///
/// Runs one node with public API address 127.0.0.1:8080, private API address 127.0.0.1:8081,
/// EJB port 6400.
#[structopt(name = "run-dev")]
RunDev(RunDev),
/// Perform different maintenance actions.
#[structopt(name = "maintenance")]
Maintenance(Maintenance),
Expand All @@ -66,6 +76,7 @@ impl EjbCommand for Command {
Command::GenerateConfig(c) => c.execute().map(Into::into),
Command::Finalize(c) => c.execute().map(Into::into),
Command::Run(c) => c.execute(),
Command::RunDev(c) => c.execute(),
Command::Maintenance(c) => c.execute().map(Into::into),
}
}
Expand Down Expand Up @@ -114,6 +125,84 @@ pub struct Run {
jvm_args_append: Vec<String>,
}

/// EJB-specific `run-dev` command.
///
/// Automatically generates node configuration for one
/// validator and runs it using provided `artifacts_path` as a directory for Java service artifacts.
#[derive(Debug, StructOpt, Serialize, Deserialize)]
#[structopt(rename_all = "kebab-case")]
pub struct RunDev {
/// Path to the directory containing Java service artifacts.
#[structopt(long)]
artifacts_path: PathBuf,
/// Path to a directory for blockchain database and configuration files.
///
/// Database is located in <blockchain_path>/db directory, node configuration files
/// are located in <blockchain_path>/config directory. Existing files and directories are
/// reused. To generate new node configuration and start a new blockchain, the user must
/// manually delete existing <blockchain_path> directory or specify a new one.
#[structopt(long)]
blockchain_path: PathBuf,
/// Path to log4j configuration file.
#[structopt(long)]
ejb_log_config_path: Option<PathBuf>,
}

impl RunDev {
/// Automatically generates node configuration and returns a path to node configuration file.
///
/// Does not alter existing configuration files.
fn generate_node_configuration_if_needed(&self) -> Result<PathBuf, failure::Error> {
let config_directory = concat_path(self.blockchain_path.clone(), "config");
let node_config_path = concat_path(config_directory.clone(), "node.toml");

// Configuration files exist, skip generation.
if config_directory.exists() {
Copy link
Contributor

Choose a reason for hiding this comment

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

How does existence of a config_directory (a temp dir may be created in config_directory) imply config file existence? Have you tested this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Error messages when node configuration file is deleted from database_path/config directory:

Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }

loading config from testnet/config/node.toml

return Ok(node_config_path);
}

let validators_count = 1;
let peer_address = "127.0.0.1:6200".parse().unwrap();
let public_api_address = "127.0.0.1:8080".parse().unwrap();
let private_api_address = "127.0.0.1:8081".parse().unwrap();
let public_allow_origin = "http://127.0.0.1:8080, http://localhost:8080".into();
let private_allow_origin = "http://127.0.0.1:8081, http://localhost:8081".into();
let common_config_path = concat_path(config_directory.clone(), "template.toml");
let public_config_path = concat_path(config_directory.clone(), PUB_CONFIG_FILE_NAME);
let secret_config_path = concat_path(config_directory.clone(), SEC_CONFIG_FILE_NAME);

let generate_template = GenerateTemplate {
common_config: common_config_path.clone(),
validators_count,
};
generate_template.execute()?;

let generate_config = GenerateConfig {
common_config: common_config_path.clone(),
output_dir: config_directory.clone(),
peer_address,
listen_address: None,
no_password: true,
master_key_pass: None,
master_key_path: None,
};
generate_config.execute()?;

let finalize = Finalize {
secret_config_path,
output_config_path: node_config_path.clone(),
public_configs: vec![public_config_path],
public_api_address: Some(public_api_address),
private_api_address: Some(private_api_address),
public_allow_origin: Some(public_allow_origin),
private_allow_origin: Some(private_allow_origin),
};
finalize.execute()?;

Ok(node_config_path)
}
}

/// Possible output of the Java Bindings CLI commands.
pub enum EjbCommandResult {
/// Output of the standard Exonum Core commands.
Expand Down Expand Up @@ -171,10 +260,47 @@ impl EjbCommand for Run {
}
}

impl EjbCommand for RunDev {
fn execute(self) -> Result<EjbCommandResult, failure::Error> {
let db_path = concat_path(self.blockchain_path.clone(), "db");
let node_config_path = self.generate_node_configuration_if_needed()?;

let ejb_port = 6400;

let standard_run = StandardRun {
node_config: node_config_path,
db_path,
public_api_address: None,
private_api_address: None,
master_key_pass: Some(FromStr::from_str("pass:").unwrap()),
};

let run = Run {
standard: standard_run,
ejb_port,
artifacts_path: self.artifacts_path,
ejb_log_config_path: self.ejb_log_config_path,
ejb_override_java_library_path: None,
jvm_debug: None,
jvm_args_prepend: vec![],
jvm_args_append: vec![],
};

run.execute()
}
}

/// Returns full path to the default log configuration file assuming the `exonum-java` app is
/// packaged/installed.
fn get_path_to_default_log_config() -> PathBuf {
let mut path = executable_directory();
path.push("log4j-fallback.xml");
path
}

/// Concatenates PathBuf and string. Useful to make a `PathBuf` to a file in the specific directory.
fn concat_path(first: PathBuf, second: &str) -> PathBuf {
let mut path = first;
path.push(second);
path
}