-
Notifications
You must be signed in to change notification settings - Fork 98
Configure replica via dfx config file. #452
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
Changes from all commits
03129c7
a9326d7
447ae1e
dde8347
463df32
f6ac7af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,110 +1,142 @@ | ||
| use crate::commands::canister::create_waiter; | ||
| use crate::config::dfinity::ConfigDefaultsReplica; | ||
| use crate::lib::environment::Environment; | ||
| use crate::lib::error::{DfxError, DfxResult}; | ||
| use crate::lib::message::UserMessage; | ||
| use crate::lib::replica_config::{ReplicaConfig, SchedulerConfig}; | ||
| use crate::lib::replica_config::{ | ||
| HttpHandlerConfig, ReplicaConfig, SchedulerConfig, StateManagerConfig, | ||
| }; | ||
|
|
||
| use clap::{App, Arg, ArgMatches, SubCommand}; | ||
| use crossbeam::channel::{Receiver, Sender}; | ||
| use crossbeam::unbounded; | ||
| use ic_http_agent::{Agent, AgentConfig}; | ||
| use indicatif::{ProgressBar, ProgressDrawTarget}; | ||
| use std::default::Default; | ||
| use std::io::{Error, ErrorKind}; | ||
| use std::path::PathBuf; | ||
| use std::time::Duration; | ||
| use sysinfo::{Pid, Process, ProcessExt, Signal}; | ||
| use tokio::runtime::Runtime; | ||
|
|
||
| /// Provide necessary arguments to start the Internet Computer | ||
| /// locally. See `exec` for further information. | ||
| /// Constructs a sub-command to run the Internet Computer replica. | ||
| pub fn construct() -> App<'static, 'static> { | ||
| SubCommand::with_name("replica") | ||
| .about(UserMessage::Replica.to_str()) | ||
| .arg( | ||
| Arg::with_name("message-gas-limit") | ||
| .help(UserMessage::ReplicaMessageGasLimit.to_str()) | ||
| .long("message-gas-limit") | ||
| .takes_value(true), | ||
| ) | ||
| .arg( | ||
| Arg::with_name("port") | ||
| .help(UserMessage::ReplicaPort.to_str()) | ||
| .long("port") | ||
| .takes_value(true) | ||
| .default_value("8080") | ||
| .validator(|v| { | ||
| v.parse::<u16>() | ||
| .map_err(|_| "Must pass a valid port number.".to_owned()) | ||
| .map(|_| ()) | ||
| }), | ||
| ) | ||
| .arg( | ||
| Arg::with_name("message-gas-max") | ||
| .help(UserMessage::ReplicaMessageGasMax.to_str()) | ||
| .long("message-gas-max") | ||
| .takes_value(true) | ||
| .default_value("5368709120"), | ||
| .takes_value(true), | ||
| ) | ||
| .arg( | ||
| Arg::with_name("round-gas-max") | ||
| .help(UserMessage::ReplicaRoundGasMax.to_str()) | ||
| .long("round-gas-max") | ||
| .takes_value(true) | ||
| .default_value("26843545600"), | ||
| Arg::with_name("round-gas-limit") | ||
| .help(UserMessage::ReplicaRoundGasLimit.to_str()) | ||
| .long("round-gas-limit") | ||
| .takes_value(true), | ||
| ) | ||
| } | ||
|
|
||
| /// Gets the configuration options for the Internet Computer replica. | ||
| fn get_config(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult<ReplicaConfig> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to avoid that an move to using Claps' proc macros instead. This code makes everything more obscure.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate. The logic here is exactly the same as https://github.com/dfinity-lab/sdk/blob/6ba57a72ca7fc3d28741b0fae3884a3722b5bbd5/src/dfx/src/commands/bootstrap.rs#L88 |
||
| let config = get_config_from_file(env); | ||
| let port = get_port(&config, args)?; | ||
| let mut http_handler: HttpHandlerConfig = Default::default(); | ||
| if port == 0 { | ||
| let file = env.get_temp_dir().join("config").join("port.txt"); | ||
| http_handler.write_port_to = Some(file); | ||
| } else { | ||
| http_handler.use_port = Some(port); | ||
| }; | ||
| let message_gas_limit = get_message_gas_limit(&config, args)?; | ||
| let round_gas_limit = get_round_gas_limit(&config, args)?; | ||
| let scheduler = SchedulerConfig { | ||
| exec_gas: Some(message_gas_limit), | ||
| round_gas_max: Some(round_gas_limit), | ||
| } | ||
| .validate()?; | ||
| let state_manager = StateManagerConfig { | ||
| state_root: env.get_state_dir(), | ||
| }; | ||
| Ok(ReplicaConfig { | ||
| http_handler, | ||
| scheduler, | ||
| state_manager, | ||
| }) | ||
| } | ||
|
|
||
| /// Gets the configuration options for the Internet Computer replica as they were specified in the | ||
| /// dfx configuration file. | ||
| fn get_config_from_file(env: &dyn Environment) -> ConfigDefaultsReplica { | ||
| env.get_config().map_or(Default::default(), |config| { | ||
| config.get_config().get_defaults().get_replica().to_owned() | ||
| }) | ||
| } | ||
|
|
||
| /// Gets the port number that the Internet Computer replica listens on. First checks if the port | ||
| /// number was specified on the command-line using --port, otherwise checks if the port number was | ||
| /// specified in the dfx configuration file, otherise defaults to 8080. | ||
| fn get_port(config: &ConfigDefaultsReplica, args: &ArgMatches<'_>) -> DfxResult<u16> { | ||
| args.value_of("port") | ||
| .map(|port| port.parse()) | ||
| .unwrap_or_else(|| { | ||
| let default = 8080; | ||
| Ok(config.port.unwrap_or(default)) | ||
| }) | ||
| .map_err(|err| DfxError::InvalidArgument(format!("Invalid port number: {}", err))) | ||
| } | ||
|
|
||
| /// Gets the maximum amount of gas a single message can consume. First checks if the gas limit was | ||
| /// specified on the command-line using --message-gas-limit, otherwise checks if the gas limit was | ||
| /// specified in the dfx configuration file, otherise defaults to 5368709120. | ||
| fn get_message_gas_limit(config: &ConfigDefaultsReplica, args: &ArgMatches<'_>) -> DfxResult<u64> { | ||
| args.value_of("message-gas-limit") | ||
| .map(|limit| limit.parse()) | ||
| .unwrap_or_else(|| { | ||
| let default = 5_368_709_120; | ||
| Ok(config.message_gas_limit.unwrap_or(default)) | ||
| }) | ||
| .map_err(|err| DfxError::InvalidArgument(format!("Invalid message gas limit: {}", err))) | ||
| } | ||
|
|
||
| /// Gets the maximum amount of gas a single round can consume. First checks if the gas limit was | ||
| /// specified on the command-line using --round-gas-limit, otherwise checks if the gas limit was | ||
| /// specified in the dfx configuration file, otherise defaults to 26843545600. | ||
| fn get_round_gas_limit(config: &ConfigDefaultsReplica, args: &ArgMatches<'_>) -> DfxResult<u64> { | ||
| args.value_of("round-gas-limit") | ||
| .map(|limit| limit.parse()) | ||
| .unwrap_or_else(|| { | ||
| let default = 26_843_545_600; | ||
| Ok(config.round_gas_limit.unwrap_or(default)) | ||
| }) | ||
| .map_err(|err| DfxError::InvalidArgument(format!("Invalid round gas limit: {}", err))) | ||
| } | ||
|
|
||
| fn ping_and_wait(frontend_url: &str) -> DfxResult { | ||
| let mut runtime = Runtime::new().expect("Unable to create a runtime"); | ||
|
|
||
| let agent = Agent::new(AgentConfig { | ||
| url: frontend_url, | ||
| ..AgentConfig::default() | ||
| })?; | ||
|
|
||
| runtime | ||
| .block_on(agent.ping(create_waiter())) | ||
| .map_err(DfxError::from) | ||
| } | ||
|
|
||
| fn get_scheduler(args: &ArgMatches<'_>) -> DfxResult<SchedulerConfig> { | ||
| // Get mssage gas limit. | ||
| let message_gas_max = args | ||
| .value_of("message-gas-max") | ||
| .expect("default value") | ||
| .parse() | ||
| .map_err(|err| DfxError::InvalidArgument(format!("Invalid message gas limit: {}", err)))?; | ||
| // Get round gas limit. | ||
| let round_gas_max = args | ||
| .value_of("round-gas-max") | ||
| .expect("default value") | ||
| .parse() | ||
| .map_err(|err| DfxError::InvalidArgument(format!("Invalid round gas limit: {}", err)))?; | ||
| // Check message and round gas limits. | ||
| if message_gas_max >= round_gas_max { | ||
| let err = "Round gas limit must exceed message gas limit.".to_string(); | ||
| Err(DfxError::InvalidArgument(err)) | ||
| } else { | ||
| // Return scheduler configuration. | ||
| Ok(SchedulerConfig { | ||
| exec_gas: Some(message_gas_max), | ||
| round_gas_max: Some(round_gas_max), | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // TODO(eftychis)/In progress: Rename to replica. | ||
| /// Start the Internet Computer locally. Spawns a proxy to forward and | ||
| /// manage browser requests. Responsible for running the network (one | ||
| /// replica at the moment) and the proxy. | ||
| pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { | ||
| let replica_binary_path = env.get_cache().get_binary_command_path("replica")?; | ||
| let temp_dir = env.get_temp_dir(); | ||
| let state_root = env.get_state_dir(); | ||
| let pid_file_path = temp_dir.join("pid"); | ||
|
|
||
| let port = args | ||
| .value_of("port") | ||
| .unwrap_or("8080") | ||
| .parse::<u16>() | ||
| .expect("Unreachable. Port should have been validated by clap."); | ||
|
|
||
| let scheduler = get_scheduler(args)?; | ||
|
|
||
| // We are doing this here to make sure we can write to the temp | ||
| // pid file. | ||
| std::fs::write(&pid_file_path, "")?; | ||
|
|
@@ -121,10 +153,9 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { | |
| let (_broadcast_stop, is_killed_replica) = unbounded(); | ||
|
|
||
| b.set_message("Generating IC local replica configuration."); | ||
| let replica_config = ReplicaConfig::new(&state_root) | ||
| .with_port(port) | ||
| .with_scheduler(scheduler) | ||
| .to_toml()?; | ||
| let config = get_config(env, args)?; | ||
| let port = config.http_handler.use_port.expect("non-random port"); | ||
| let toml = config.to_toml()?; | ||
|
|
||
| // TODO(eftychis): we need a proper manager type when we start | ||
| // spawning multiple replica processes and registry. | ||
|
|
@@ -137,7 +168,7 @@ pub fn exec(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult { | |
| &pid_file_path, | ||
| is_killed_replica, | ||
| request_stop, | ||
| replica_config, | ||
| toml, | ||
| b, | ||
| ) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are those removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't want command-line argument defaults when considering the dfx configuration file. The strategy is to first check if the configuration option was provided on the command line, and if it wasn't, then check if the configuration option was specified in the dfx configuration file, otherwise resort to the default value. If you use command-line argument defaults, then you short circuit the dfx configuration file.