Merge #365: gui: make daemon config optional

9577e6a227a85aa3a2f337a52b0f2904ed836bdf gui: make daemon config optional (edouard)

Pull request description:

  If no daemon config path is present in the
  gui configuration file, gui will try first to connect
  to the socket path, either `daemon_rpc_path`
  if present in configuration file or default
  path.

  close #147

ACKs for top commit:
  edouardparis:
    Self-ACK 9577e6a227a85aa3a2f337a52b0f2904ed836bdf

Tree-SHA512: 85e95dc3e4d5c0e4642cd75387fffd17ddd23e14459644238fed82cb8477bee27ae4e1f603d3dd2a7615ec8e397d9f50e2239a9c1ced49892cb02bd8cc5f748b
This commit is contained in:
edouard 2023-03-14 16:19:11 +01:00
commit 2c7cd2b0ca
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
5 changed files with 85 additions and 50 deletions

View File

@ -6,7 +6,9 @@ use tracing_subscriber::filter;
#[derive(Debug, Clone, Deserialize, Serialize)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config { pub struct Config {
/// Path to lianad configuration file. /// Path to lianad configuration file.
pub daemon_config_path: PathBuf, pub daemon_config_path: Option<PathBuf>,
/// Path to lianad_rpc socket file.
pub daemon_rpc_path: Option<PathBuf>,
/// log level, can be "info", "debug", "trace". /// log level, can be "info", "debug", "trace".
pub log_level: Option<String>, pub log_level: Option<String>,
/// Use iced debug feature if true. /// Use iced debug feature if true.
@ -21,7 +23,8 @@ pub const DEFAULT_FILE_NAME: &str = "gui.toml";
impl Config { impl Config {
pub fn new(daemon_config_path: PathBuf) -> Self { pub fn new(daemon_config_path: PathBuf) -> Self {
Self { Self {
daemon_config_path, daemon_config_path: Some(daemon_config_path),
daemon_rpc_path: None,
log_level: None, log_level: None,
debug: None, debug: None,
hardware_wallets: None, hardware_wallets: None,

View File

@ -11,6 +11,7 @@ mod error;
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -138,7 +139,10 @@ impl App {
) )
} }
Message::LoadDaemonConfig(cfg) => { Message::LoadDaemonConfig(cfg) => {
let res = self.load_daemon_config(*cfg); let path = self.config.daemon_config_path.clone().expect(
"Application config must have a daemon configuration file path at this point.",
);
let res = self.load_daemon_config(&path, *cfg);
self.update(Message::DaemonConfigLoaded(res)) self.update(Message::DaemonConfigLoaded(res))
} }
Message::View(view::Message::Menu(menu)) => self.load_state(&menu), Message::View(view::Message::Menu(menu)) => self.load_state(&menu),
@ -147,7 +151,11 @@ impl App {
} }
} }
pub fn load_daemon_config(&mut self, cfg: DaemonConfig) -> Result<(), Error> { pub fn load_daemon_config(
&mut self,
daemon_config_path: &PathBuf,
cfg: DaemonConfig,
) -> Result<(), Error> {
loop { loop {
if let Some(daemon) = Arc::get_mut(&mut self.daemon) { if let Some(daemon) = Arc::get_mut(&mut self.daemon) {
daemon.load_config(cfg)?; daemon.load_config(cfg)?;
@ -157,7 +165,7 @@ impl App {
let mut daemon_config_file = OpenOptions::new() let mut daemon_config_file = OpenOptions::new()
.write(true) .write(true)
.open(&self.config.daemon_config_path) .open(daemon_config_path)
.map_err(|e| Error::Config(e.to_string()))?; .map_err(|e| Error::Config(e.to_string()))?;
let content = let content =

View File

@ -69,13 +69,9 @@ impl Launcher {
path.push(network.to_string()); path.push(network.to_string());
path.push(app::config::DEFAULT_FILE_NAME); path.push(app::config::DEFAULT_FILE_NAME);
let cfg = app::Config::from_file(&path).expect("Already checked"); let cfg = app::Config::from_file(&path).expect("Already checked");
let daemon_cfg = Command::perform(async move { (datadir_path.clone(), cfg, network) }, |m| {
liana::config::Config::from_file(Some(cfg.daemon_config_path.clone())) Message::Run(m.0, m.1, m.2)
.expect("Already checked"); })
Command::perform(
async move { (datadir_path.clone(), cfg, daemon_cfg) },
|m| Message::Run(m.0, m.1, m.2),
)
} }
}, },
_ => Command::none(), _ => Command::none(),
@ -157,7 +153,7 @@ pub enum Message {
View(ViewMessage), View(ViewMessage),
Install(PathBuf), Install(PathBuf),
Checked(Result<Network, String>), Checked(Result<Network, String>),
Run(PathBuf, app::config::Config, liana::config::Config), Run(PathBuf, app::config::Config, Network),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -177,12 +173,13 @@ async fn check_network_datadir(mut path: PathBuf, network: Network) -> Result<Ne
) )
})?; })?;
liana::config::Config::from_file(Some(cfg.daemon_config_path.clone())).map_err(|e| match e { if let Some(daemon_config_path) = cfg.daemon_config_path {
liana::config::Config::from_file(Some(daemon_config_path.clone())).map_err(|e| match e {
ConfigError::FileNotFound ConfigError::FileNotFound
| ConfigError::DatadirNotFound => { | ConfigError::DatadirNotFound => {
format!( format!(
"Failed to read daemon configuration file in the directory: {}", "Failed to read daemon configuration file in the directory: {}",
cfg.daemon_config_path.to_string_lossy() daemon_config_path.to_string_lossy()
) )
} }
ConfigError::ReadingFile(e) => { ConfigError::ReadingFile(e) => {
@ -191,7 +188,7 @@ async fn check_network_datadir(mut path: PathBuf, network: Network) -> Result<Ne
} else { } else {
format!( format!(
"Failed to read daemon configuration file in the directory: {}", "Failed to read daemon configuration file in the directory: {}",
cfg.daemon_config_path.to_string_lossy() daemon_config_path.to_string_lossy()
) )
} }
} }
@ -205,6 +202,7 @@ async fn check_network_datadir(mut path: PathBuf, network: Network) -> Result<Ne
) )
} }
})?; })?;
}
Ok(network) Ok(network)
} }

View File

@ -41,7 +41,6 @@ pub struct Loader {
pub gui_config: GUIConfig, pub gui_config: GUIConfig,
pub daemon_started: bool, pub daemon_started: bool,
daemon_config: Config,
step: Step, step: Step,
} }
@ -70,20 +69,22 @@ impl Loader {
pub fn new( pub fn new(
datadir_path: PathBuf, datadir_path: PathBuf,
gui_config: GUIConfig, gui_config: GUIConfig,
daemon_config: Config, network: bitcoin::Network,
) -> (Self, Command<Message>) { ) -> (Self, Command<Message>) {
let path = socket_path(&datadir_path, daemon_config.bitcoin_config.network); let path = gui_config
let network = daemon_config.bitcoin_config.network; .daemon_rpc_path
.clone()
.unwrap_or_else(|| socket_path(&datadir_path, network));
let network = network;
( (
Loader { Loader {
network, network,
datadir_path, datadir_path,
daemon_config: daemon_config.clone(),
gui_config, gui_config,
step: Step::Connecting, step: Step::Connecting,
daemon_started: false, daemon_started: false,
}, },
Command::perform(connect(path, daemon_config), Message::Loaded), Command::perform(connect(path), Message::Loaded),
) )
} }
@ -103,12 +104,16 @@ impl Loader {
Error::Daemon(DaemonError::ClientNotSupported) Error::Daemon(DaemonError::ClientNotSupported)
| Error::Daemon(DaemonError::Transport(Some(ErrorKind::ConnectionRefused), _)) | Error::Daemon(DaemonError::Transport(Some(ErrorKind::ConnectionRefused), _))
| Error::Daemon(DaemonError::Transport(Some(ErrorKind::NotFound), _)) => { | Error::Daemon(DaemonError::Transport(Some(ErrorKind::NotFound), _)) => {
if let Some(daemon_config_path) = self.gui_config.daemon_config_path.clone() {
self.step = Step::StartingDaemon; self.step = Step::StartingDaemon;
self.daemon_started = true; self.daemon_started = true;
return Command::perform( return Command::perform(
start_daemon(self.gui_config.daemon_config_path.clone()), start_daemon(daemon_config_path),
Message::Started, Message::Started,
); );
} else {
self.step = Step::Error(Box::new(e));
}
} }
_ => { _ => {
self.step = Step::Error(Box::new(e)); self.step = Step::Error(Box::new(e));
@ -187,7 +192,7 @@ impl Loader {
let (loader, cmd) = Self::new( let (loader, cmd) = Self::new(
self.datadir_path.clone(), self.datadir_path.clone(),
self.gui_config.clone(), self.gui_config.clone(),
self.daemon_config.clone(), self.network,
); );
*self = loader; *self = loader;
cmd cmd
@ -361,10 +366,7 @@ pub fn cover<'a, T: 'a + Clone, C: Into<Element<'a, T>>>(
.into() .into()
} }
async fn connect( async fn connect(socket_path: PathBuf) -> Result<Arc<dyn Daemon + Sync + Send>, Error> {
socket_path: PathBuf,
_config: Config,
) -> Result<Arc<dyn Daemon + Sync + Send>, Error> {
let client = client::jsonrpc::JsonRPCClient::new(socket_path); let client = client::jsonrpc::JsonRPCClient::new(socket_path);
let daemon = Lianad::new(client); let daemon = Lianad::new(client);

View File

@ -123,15 +123,13 @@ impl Application for GUI {
]), ]),
) )
} }
Config::Run(datadir_path, cfg) => { Config::Run(datadir_path, cfg, network) => {
let daemon_cfg =
DaemonConfig::from_file(Some(cfg.daemon_config_path.clone())).unwrap();
logger.set_running_mode( logger.set_running_mode(
datadir_path.clone(), datadir_path.clone(),
daemon_cfg.bitcoin_config.network, network,
cfg.log_level().unwrap_or(LevelFilter::INFO), cfg.log_level().unwrap_or(LevelFilter::INFO),
); );
let (loader, command) = Loader::new(datadir_path, cfg, daemon_cfg); let (loader, command) = Loader::new(datadir_path, cfg, network);
( (
Self { Self {
state: State::Loader(Box::new(loader)), state: State::Loader(Box::new(loader)),
@ -172,13 +170,13 @@ impl Application for GUI {
self.state = State::Installer(Box::new(install)); self.state = State::Installer(Box::new(install));
command.map(|msg| Message::Install(Box::new(msg))) command.map(|msg| Message::Install(Box::new(msg)))
} }
launcher::Message::Run(datadir_path, cfg, daemon_cfg) => { launcher::Message::Run(datadir_path, cfg, network) => {
self.logger.set_running_mode( self.logger.set_running_mode(
datadir_path.clone(), datadir_path.clone(),
daemon_cfg.bitcoin_config.network, network,
cfg.log_level().unwrap_or(LevelFilter::INFO), cfg.log_level().unwrap_or(LevelFilter::INFO),
); );
let (loader, command) = Loader::new(datadir_path, cfg, daemon_cfg); let (loader, command) = Loader::new(datadir_path, cfg, network);
self.state = State::Loader(Box::new(loader)); self.state = State::Loader(Box::new(loader));
command.map(|msg| Message::Load(Box::new(msg))) command.map(|msg| Message::Load(Box::new(msg)))
} }
@ -188,7 +186,7 @@ impl Application for GUI {
if let installer::Message::Exit(path) = *msg { if let installer::Message::Exit(path) = *msg {
let cfg = app::Config::from_file(&path).unwrap(); let cfg = app::Config::from_file(&path).unwrap();
let daemon_cfg = let daemon_cfg =
DaemonConfig::from_file(Some(cfg.daemon_config_path.clone())).unwrap(); DaemonConfig::from_file(cfg.daemon_config_path.clone()).unwrap();
let datadir_path = daemon_cfg let datadir_path = daemon_cfg
.data_dir .data_dir
.as_ref() .as_ref()
@ -201,7 +199,8 @@ impl Application for GUI {
cfg.log_level().unwrap_or(LevelFilter::INFO), cfg.log_level().unwrap_or(LevelFilter::INFO),
); );
self.logger.remove_install_log_file(datadir_path.clone()); self.logger.remove_install_log_file(datadir_path.clone());
let (loader, command) = Loader::new(datadir_path, cfg, daemon_cfg); let (loader, command) =
Loader::new(datadir_path, cfg, daemon_cfg.bitcoin_config.network);
self.state = State::Loader(Box::new(loader)); self.state = State::Loader(Box::new(loader));
command.map(|msg| Message::Load(Box::new(msg))) command.map(|msg| Message::Load(Box::new(msg)))
} else { } else {
@ -255,7 +254,7 @@ impl Application for GUI {
} }
pub enum Config { pub enum Config {
Run(PathBuf, app::Config), Run(PathBuf, app::Config, bitcoin::Network),
Launcher(PathBuf), Launcher(PathBuf),
Install(PathBuf, bitcoin::Network), Install(PathBuf, bitcoin::Network),
} }
@ -270,7 +269,7 @@ impl Config {
path.push(network.to_string()); path.push(network.to_string());
path.push(app::config::DEFAULT_FILE_NAME); path.push(app::config::DEFAULT_FILE_NAME);
match app::Config::from_file(&path) { match app::Config::from_file(&path) {
Ok(cfg) => Ok(Config::Run(datadir_path, cfg)), Ok(cfg) => Ok(Config::Run(datadir_path, cfg, network)),
Err(ConfigError::NotFound) => Ok(Config::Install(datadir_path, network)), Err(ConfigError::NotFound) => Ok(Config::Install(datadir_path, network)),
Err(e) => Err(format!("Failed to read configuration file: {}", e).into()), Err(e) => Err(format!("Failed to read configuration file: {}", e).into()),
} }
@ -295,11 +294,36 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
[Arg::ConfigPath(path)] => { [Arg::ConfigPath(path)] => {
let cfg = app::Config::from_file(path)?; let cfg = app::Config::from_file(path)?;
let daemon_cfg = DaemonConfig::from_file(Some(cfg.daemon_config_path.clone()))?; if let Some(daemon_config_path) = cfg.daemon_config_path.clone() {
let daemon_cfg = DaemonConfig::from_file(Some(daemon_config_path))?;
let datadir_path = daemon_cfg let datadir_path = daemon_cfg
.data_dir .data_dir
.unwrap_or_else(|| default_datadir().unwrap()); .unwrap_or_else(|| default_datadir().unwrap());
Ok(Config::Run(datadir_path, cfg)) Ok(Config::Run(
datadir_path,
cfg,
daemon_cfg.bitcoin_config.network,
))
} else {
Err("Application cannot guess network".into())
}
}
[Arg::ConfigPath(path), Arg::Network(network)]
| [Arg::Network(network), Arg::ConfigPath(path)] => {
let cfg = app::Config::from_file(path)?;
if let Some(daemon_config_path) = cfg.daemon_config_path.clone() {
let daemon_cfg = DaemonConfig::from_file(Some(daemon_config_path))?;
let datadir_path = daemon_cfg
.data_dir
.unwrap_or_else(|| default_datadir().unwrap());
Ok(Config::Run(
datadir_path,
cfg,
daemon_cfg.bitcoin_config.network,
))
} else {
Ok(Config::Run(default_datadir().unwrap(), cfg, *network))
}
} }
[Arg::DatadirPath(datadir_path)] => Config::new(datadir_path.clone(), None), [Arg::DatadirPath(datadir_path)] => Config::new(datadir_path.clone(), None),
[Arg::DatadirPath(datadir_path), Arg::Network(network)] [Arg::DatadirPath(datadir_path), Arg::Network(network)]