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:
commit
2c7cd2b0ca
@ -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,
|
||||||
|
|||||||
@ -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 =
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|
||||||
|
|||||||
@ -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)]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user