* [pve-devel] [PATCH installer 0/7] refactor; improve installation progress @ 2023-07-26 14:03 Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 1/7] tui: move install progress dialog into own view module Christoph Heiss ` (6 more replies) 0 siblings, 7 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:03 UTC (permalink / raw) To: pve-devel First 5 patches splits out the gigantic install progress dialog into its own module; then applying some divide-and-conquer to make it more readable and get the inner >150 lines closure down to a more maintainable state. Patch 6 does some code de-duplication for the low-lever installer. Patch 7 improves the auto-reboot message/UX by counting down the timeout, to match its GUI counterpart. Christoph Heiss (7): tui: move install progress dialog into own view module tui: install progress: split out low-level installer spawing into own function tui: install progress: split out reboot handling into own function tui: install progress: split out prompt logic into own function tui: install progress: handle errors in ui message loop more gracefully low-level: avoid open-coding config reading, parsing and merging low-level, tui: count down auto-reboot timeout proxmox-low-level-installer | 70 +++-- proxmox-tui-installer/src/main.rs | 235 +-------------- .../src/views/install_progress.rs | 275 ++++++++++++++++++ proxmox-tui-installer/src/views/mod.rs | 3 + 4 files changed, 318 insertions(+), 265 deletions(-) create mode 100644 proxmox-tui-installer/src/views/install_progress.rs -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH installer 1/7] tui: move install progress dialog into own view module 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss @ 2023-07-26 14:03 ` Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 2/7] tui: install progress: split out low-level installer spawing into own function Christoph Heiss ` (5 subsequent siblings) 6 siblings, 0 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:03 UTC (permalink / raw) To: pve-devel While doing so, also pull the main progress thread task into a separate function, as well as the spawning of the low-level installer. No functional changes. Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- proxmox-tui-installer/src/main.rs | 235 +--------------- .../src/views/install_progress.rs | 251 ++++++++++++++++++ proxmox-tui-installer/src/views/mod.rs | 3 + 3 files changed, 261 insertions(+), 228 deletions(-) create mode 100644 proxmox-tui-installer/src/views/install_progress.rs diff --git a/proxmox-tui-installer/src/main.rs b/proxmox-tui-installer/src/main.rs index 7bfaf9b..a356f6d 100644 --- a/proxmox-tui-installer/src/main.rs +++ b/proxmox-tui-installer/src/main.rs @@ -1,24 +1,12 @@ -use std::{ - collections::HashMap, - env, - io::{BufRead, BufReader, Write}, - net::IpAddr, - path::PathBuf, - str::FromStr, - sync::{Arc, Mutex}, - thread, - time::Duration, -}; +use std::{collections::HashMap, env, net::IpAddr, path::PathBuf}; use cursive::{ event::Event, theme::{ColorStyle, Effect, PaletteColor, Style}, - utils::Counter, view::{Nameable, Offset, Resizable, ViewWrapper}, views::{ Button, Checkbox, Dialog, DummyView, EditView, Layer, LinearLayout, PaddedView, Panel, - ProgressBar, ResizedView, ScrollView, SelectView, StackView, TextContent, TextView, - ViewRef, + ResizedView, ScrollView, SelectView, StackView, TextView, ViewRef, }, Cursive, CursiveRunnable, ScreenId, View, XY, }; @@ -29,7 +17,7 @@ mod options; use options::*; mod setup; -use setup::{InstallConfig, LocaleInfo, ProxmoxProduct, RuntimeInfo, SetupInfo}; +use setup::{LocaleInfo, ProxmoxProduct, RuntimeInfo, SetupInfo}; mod system; @@ -38,8 +26,8 @@ use utils::Fqdn; mod views; use views::{ - BootdiskOptionsView, CidrAddressEditView, FormView, TableView, TableViewItem, - TimezoneOptionsView, + BootdiskOptionsView, CidrAddressEditView, FormView, InstallProgressView, TableView, + TableViewItem, TimezoneOptionsView, }; // TextView::center() seems to garble the first two lines, so fix it manually here. @@ -720,215 +708,6 @@ fn install_progress_dialog(siv: &mut Cursive) -> InstallerView { // Ensure the screen is updated independently of keyboard events and such siv.set_autorefresh(true); - let cb_sink = siv.cb_sink().clone(); - let state = siv.user_data::<InstallerState>().unwrap(); - let progress_text = TextContent::new("starting the installation .."); - - let progress_task = { - let progress_text = progress_text.clone(); - let options = state.options.clone(); - move |counter: Counter| { - let child = { - use std::process::{Command, Stdio}; - - #[cfg(not(debug_assertions))] - let (path, args, envs): (&str, [&str; 1], [(&str, &str); 0]) = - ("proxmox-low-level-installer", ["start-session"], []); - - #[cfg(debug_assertions)] - let (path, args, envs) = ( - PathBuf::from("./proxmox-low-level-installer"), - ["-t", "start-session-test"], - [("PERL5LIB", ".")], - ); - - Command::new(path) - .args(args) - .envs(envs) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - }; - - let mut child = match child { - Ok(child) => child, - Err(err) => { - let _ = cb_sink.send(Box::new(move |siv| { - siv.add_layer( - Dialog::text(err.to_string()) - .title("Error") - .button("Ok", Cursive::quit), - ); - })); - return; - } - }; - - let inner = || { - let reader = child.stdout.take().map(BufReader::new)?; - let mut writer = child.stdin.take()?; - - serde_json::to_writer(&mut writer, &InstallConfig::from(options)).unwrap(); - writeln!(writer).unwrap(); - - let writer = Arc::new(Mutex::new(writer)); - - for line in reader.lines() { - let line = match line { - Ok(line) => line, - Err(_) => break, - }; - - let msg = match line.parse::<UiMessage>() { - Ok(msg) => msg, - Err(stray) => { - eprintln!("low-level installer: {stray}"); - continue; - } - }; - - match msg { - UiMessage::Info(s) => cb_sink.send(Box::new(|siv| { - siv.add_layer(Dialog::info(s).title("Information")); - })), - UiMessage::Error(s) => cb_sink.send(Box::new(|siv| { - siv.add_layer(Dialog::info(s).title("Error")); - })), - UiMessage::Prompt(s) => cb_sink.send({ - let writer = writer.clone(); - Box::new(move |siv| { - yes_no_dialog( - siv, - "Prompt", - &s, - Box::new({ - let writer = writer.clone(); - move |_| { - if let Ok(mut writer) = writer.lock() { - let _ = writeln!(writer, "ok"); - } - } - }), - Box::new(move |_| { - if let Ok(mut writer) = writer.lock() { - let _ = writeln!(writer); - } - }), - ); - }) - }), - UiMessage::Progress(ratio, s) => { - counter.set(ratio); - progress_text.set_content(s); - Ok(()) - } - UiMessage::Finished(success, msg) => { - counter.set(100); - progress_text.set_content(msg.to_owned()); - cb_sink.send(Box::new(move |siv| { - let title = if success { "Success" } else { "Failure" }; - - // For rebooting, we just need to quit the installer, - // our caller does the actual reboot. - siv.add_layer( - Dialog::text(msg) - .title(title) - .button("Reboot now", Cursive::quit), - ); - - let autoreboot = siv - .user_data::<InstallerState>() - .map(|state| state.options.autoreboot) - .unwrap_or_default(); - - if autoreboot && success { - let cb_sink = siv.cb_sink(); - thread::spawn({ - let cb_sink = cb_sink.clone(); - move || { - thread::sleep(Duration::from_secs(5)); - let _ = cb_sink.send(Box::new(Cursive::quit)); - } - }); - } - })) - } - } - .unwrap(); - } - - Some(()) - }; - - if inner().is_none() { - cb_sink - .send(Box::new(|siv| { - siv.add_layer( - Dialog::text("low-level installer exited early") - .title("Error") - .button("Exit", Cursive::quit), - ); - })) - .unwrap(); - } - } - }; - - let progress_bar = ProgressBar::new().with_task(progress_task).full_width(); - let inner = PaddedView::lrtb( - 1, - 1, - 1, - 1, - LinearLayout::vertical() - .child(PaddedView::lrtb(1, 1, 0, 0, progress_bar)) - .child(DummyView) - .child(TextView::new_with_content(progress_text).center()) - .child(PaddedView::lrtb( - 1, - 1, - 1, - 0, - LinearLayout::horizontal().child(abort_install_button()), - )), - ); - - InstallerView::with_raw(state, inner) -} - -enum UiMessage { - Info(String), - Error(String), - Prompt(String), - Finished(bool, String), - Progress(usize, String), -} - -impl FromStr for UiMessage { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - let (ty, rest) = s.split_once(": ").ok_or("invalid message: no type")?; - - match ty { - "message" => Ok(UiMessage::Info(rest.to_owned())), - "error" => Ok(UiMessage::Error(rest.to_owned())), - "prompt" => Ok(UiMessage::Prompt(rest.to_owned())), - "finished" => { - let (state, rest) = rest.split_once(", ").ok_or("invalid message: no state")?; - Ok(UiMessage::Finished(state == "ok", rest.to_owned())) - } - "progress" => { - let (percent, rest) = rest.split_once(' ').ok_or("invalid progress message")?; - Ok(UiMessage::Progress( - percent - .parse::<f64>() - .map(|v| (v * 100.).floor() as usize) - .map_err(|err| err.to_string())?, - rest.to_owned(), - )) - } - unknown => Err(format!("invalid message type {unknown}, rest: {rest}")), - } - } + let state = siv.user_data::<InstallerState>().cloned().unwrap(); + InstallerView::with_raw(&state, InstallProgressView::new(siv)) } diff --git a/proxmox-tui-installer/src/views/install_progress.rs b/proxmox-tui-installer/src/views/install_progress.rs new file mode 100644 index 0000000..e9d764a --- /dev/null +++ b/proxmox-tui-installer/src/views/install_progress.rs @@ -0,0 +1,251 @@ +use std::{ + io::{self, BufRead, BufReader, Write}, + process, + str::FromStr, + sync::{Arc, Mutex}, + thread, + time::Duration, +}; + +use cursive::{ + utils::Counter, + view::{Resizable, ViewWrapper}, + views::{Dialog, DummyView, LinearLayout, PaddedView, ProgressBar, TextContent, TextView}, + CbSink, Cursive, +}; + +use crate::{ + abort_install_button, options::InstallerOptions, setup::InstallConfig, yes_no_dialog, + InstallerState, +}; + +pub struct InstallProgressView { + view: PaddedView<LinearLayout>, +} + +impl InstallProgressView { + pub fn new(siv: &mut Cursive) -> Self { + let cb_sink = siv.cb_sink().clone(); + let state = siv.user_data::<InstallerState>().unwrap(); + let progress_text = TextContent::new("starting the installation .."); + + let progress_task = { + let progress_text = progress_text.clone(); + let options = state.options.clone(); + move |counter: Counter| progress_task(counter, cb_sink, options, progress_text) + }; + + let progress_bar = ProgressBar::new().with_task(progress_task).full_width(); + let view = PaddedView::lrtb( + 1, + 1, + 1, + 1, + LinearLayout::vertical() + .child(PaddedView::lrtb(1, 1, 0, 0, progress_bar)) + .child(DummyView) + .child(TextView::new_with_content(progress_text).center()) + .child(PaddedView::lrtb( + 1, + 1, + 1, + 0, + LinearLayout::horizontal().child(abort_install_button()), + )), + ); + + Self { view } + } +} + +fn progress_task( + counter: Counter, + cb_sink: CbSink, + options: InstallerOptions, + progress_text: TextContent, +) { + let child = { + use std::process::{Command, Stdio}; + + #[cfg(not(debug_assertions))] + let (path, args, envs): (&str, [&str; 1], [(&str, &str); 0]) = + ("proxmox-low-level-installer", ["start-session"], []); + + #[cfg(debug_assertions)] + let (path, args, envs) = ( + std::path::PathBuf::from("./proxmox-low-level-installer"), + ["-t", "start-session-test"], + [("PERL5LIB", ".")], + ); + + Command::new(path) + .args(args) + .envs(envs) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + }; + + let mut child = match child { + Ok(child) => child, + Err(err) => { + let _ = cb_sink.send(Box::new(move |siv| { + siv.add_layer( + Dialog::text(err.to_string()) + .title("Error") + .button("Ok", Cursive::quit), + ); + })); + return; + } + }; + + let inner = || { + let reader = child.stdout.take().map(BufReader::new)?; + let mut writer = child.stdin.take()?; + + serde_json::to_writer(&mut writer, &InstallConfig::from(options)).unwrap(); + writeln!(writer).unwrap(); + + let writer = Arc::new(Mutex::new(writer)); + + for line in reader.lines() { + let line = match line { + Ok(line) => line, + Err(_) => break, + }; + + let msg = match line.parse::<UiMessage>() { + Ok(msg) => msg, + Err(stray) => { + eprintln!("low-level installer: {stray}"); + continue; + } + }; + + match msg { + UiMessage::Info(s) => cb_sink.send(Box::new(|siv| { + siv.add_layer(Dialog::info(s).title("Information")); + })), + UiMessage::Error(s) => cb_sink.send(Box::new(|siv| { + siv.add_layer(Dialog::info(s).title("Error")); + })), + UiMessage::Prompt(s) => cb_sink.send({ + let writer = writer.clone(); + Box::new(move |siv| { + yes_no_dialog( + siv, + "Prompt", + &s, + Box::new({ + let writer = writer.clone(); + move |_| { + if let Ok(mut writer) = writer.lock() { + let _ = writeln!(writer, "ok"); + } + } + }), + Box::new(move |_| { + if let Ok(mut writer) = writer.lock() { + let _ = writeln!(writer); + } + }), + ); + }) + }), + UiMessage::Progress(ratio, s) => { + counter.set(ratio); + progress_text.set_content(s); + Ok(()) + } + UiMessage::Finished(success, msg) => { + counter.set(100); + progress_text.set_content(msg.to_owned()); + cb_sink.send(Box::new(move |siv| { + let title = if success { "Success" } else { "Failure" }; + + // For rebooting, we just need to quit the installer, + // our caller does the actual reboot. + siv.add_layer( + Dialog::text(msg) + .title(title) + .button("Reboot now", Cursive::quit), + ); + + let autoreboot = siv + .user_data::<InstallerState>() + .map(|state| state.options.autoreboot) + .unwrap_or_default(); + + if autoreboot && success { + let cb_sink = siv.cb_sink(); + thread::spawn({ + let cb_sink = cb_sink.clone(); + move || { + thread::sleep(Duration::from_secs(5)); + let _ = cb_sink.send(Box::new(Cursive::quit)); + } + }); + } + })) + } + } + .unwrap(); + } + + Some(()) + }; + + if inner().is_none() { + cb_sink + .send(Box::new(|siv| { + siv.add_layer( + Dialog::text("low-level installer exited early") + .title("Error") + .button("Exit", Cursive::quit), + ); + })) + .unwrap(); + } +} + +impl ViewWrapper for InstallProgressView { + cursive::wrap_impl!(self.view: PaddedView<LinearLayout>); +} + +enum UiMessage { + Info(String), + Error(String), + Prompt(String), + Finished(bool, String), + Progress(usize, String), +} + +impl FromStr for UiMessage { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let (ty, rest) = s.split_once(": ").ok_or("invalid message: no type")?; + + match ty { + "message" => Ok(UiMessage::Info(rest.to_owned())), + "error" => Ok(UiMessage::Error(rest.to_owned())), + "prompt" => Ok(UiMessage::Prompt(rest.to_owned())), + "finished" => { + let (state, rest) = rest.split_once(", ").ok_or("invalid message: no state")?; + Ok(UiMessage::Finished(state == "ok", rest.to_owned())) + } + "progress" => { + let (percent, rest) = rest.split_once(' ').ok_or("invalid progress message")?; + Ok(UiMessage::Progress( + percent + .parse::<f64>() + .map(|v| (v * 100.).floor() as usize) + .map_err(|err| err.to_string())?, + rest.to_owned(), + )) + } + unknown => Err(format!("invalid message type {unknown}, rest: {rest}")), + } + } +} diff --git a/proxmox-tui-installer/src/views/mod.rs b/proxmox-tui-installer/src/views/mod.rs index aa24fa4..18dbdcf 100644 --- a/proxmox-tui-installer/src/views/mod.rs +++ b/proxmox-tui-installer/src/views/mod.rs @@ -12,6 +12,9 @@ use crate::utils::CidrAddress; mod bootdisk; pub use bootdisk::*; +mod install_progress; +pub use install_progress::*; + mod table_view; pub use table_view::*; -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH installer 2/7] tui: install progress: split out low-level installer spawing into own function 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 1/7] tui: move install progress dialog into own view module Christoph Heiss @ 2023-07-26 14:03 ` Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 3/7] tui: install progress: split out reboot handling " Christoph Heiss ` (4 subsequent siblings) 6 siblings, 0 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:03 UTC (permalink / raw) To: pve-devel No functional changes. Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- .../src/views/install_progress.rs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/proxmox-tui-installer/src/views/install_progress.rs b/proxmox-tui-installer/src/views/install_progress.rs index e9d764a..870c6a3 100644 --- a/proxmox-tui-installer/src/views/install_progress.rs +++ b/proxmox-tui-installer/src/views/install_progress.rs @@ -58,35 +58,35 @@ impl InstallProgressView { } } +fn spawn_low_level_installer() -> io::Result<process::Child> { + use std::process::{Command, Stdio}; + + #[cfg(not(debug_assertions))] + let (path, args, envs): (&str, [&str; 1], [(&str, &str); 0]) = + ("proxmox-low-level-installer", ["start-session"], []); + + #[cfg(debug_assertions)] + let (path, args, envs) = ( + std::path::PathBuf::from("./proxmox-low-level-installer"), + ["-t", "start-session-test"], + [("PERL5LIB", ".")], + ); + + Command::new(path) + .args(args) + .envs(envs) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() +} + fn progress_task( counter: Counter, cb_sink: CbSink, options: InstallerOptions, progress_text: TextContent, ) { - let child = { - use std::process::{Command, Stdio}; - - #[cfg(not(debug_assertions))] - let (path, args, envs): (&str, [&str; 1], [(&str, &str); 0]) = - ("proxmox-low-level-installer", ["start-session"], []); - - #[cfg(debug_assertions)] - let (path, args, envs) = ( - std::path::PathBuf::from("./proxmox-low-level-installer"), - ["-t", "start-session-test"], - [("PERL5LIB", ".")], - ); - - Command::new(path) - .args(args) - .envs(envs) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - }; - - let mut child = match child { + let mut child = match spawn_low_level_installer() { Ok(child) => child, Err(err) => { let _ = cb_sink.send(Box::new(move |siv| { -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH installer 3/7] tui: install progress: split out reboot handling into own function 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 1/7] tui: move install progress dialog into own view module Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 2/7] tui: install progress: split out low-level installer spawing into own function Christoph Heiss @ 2023-07-26 14:03 ` Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 4/7] tui: install progress: split out prompt logic " Christoph Heiss ` (3 subsequent siblings) 6 siblings, 0 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:03 UTC (permalink / raw) To: pve-devel No functional changes. Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- .../src/views/install_progress.rs | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/proxmox-tui-installer/src/views/install_progress.rs b/proxmox-tui-installer/src/views/install_progress.rs index 870c6a3..bd27628 100644 --- a/proxmox-tui-installer/src/views/install_progress.rs +++ b/proxmox-tui-installer/src/views/install_progress.rs @@ -80,6 +80,33 @@ fn spawn_low_level_installer() -> io::Result<process::Child> { .spawn() } +fn prepare_for_reboot(siv: &mut Cursive, success: bool, msg: &str) { + let title = if success { "Success" } else { "Failure" }; + + // For rebooting, we just need to quit the installer, our caller does the actual reboot. + siv.add_layer( + Dialog::text(msg) + .title(title) + .button("Reboot now", Cursive::quit), + ); + + let autoreboot = siv + .user_data::<InstallerState>() + .map(|state| state.options.autoreboot) + .unwrap_or_default(); + + if autoreboot && success { + let cb_sink = siv.cb_sink(); + thread::spawn({ + let cb_sink = cb_sink.clone(); + move || { + thread::sleep(Duration::from_secs(5)); + let _ = cb_sink.send(Box::new(Cursive::quit)); + } + }); + } +} + fn progress_task( counter: Counter, cb_sink: CbSink, @@ -161,33 +188,7 @@ fn progress_task( UiMessage::Finished(success, msg) => { counter.set(100); progress_text.set_content(msg.to_owned()); - cb_sink.send(Box::new(move |siv| { - let title = if success { "Success" } else { "Failure" }; - - // For rebooting, we just need to quit the installer, - // our caller does the actual reboot. - siv.add_layer( - Dialog::text(msg) - .title(title) - .button("Reboot now", Cursive::quit), - ); - - let autoreboot = siv - .user_data::<InstallerState>() - .map(|state| state.options.autoreboot) - .unwrap_or_default(); - - if autoreboot && success { - let cb_sink = siv.cb_sink(); - thread::spawn({ - let cb_sink = cb_sink.clone(); - move || { - thread::sleep(Duration::from_secs(5)); - let _ = cb_sink.send(Box::new(Cursive::quit)); - } - }); - } - })) + cb_sink.send(Box::new(move |siv| prepare_for_reboot(siv, success, &msg))) } } .unwrap(); -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH installer 4/7] tui: install progress: split out prompt logic into own function 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss ` (2 preceding siblings ...) 2023-07-26 14:03 ` [pve-devel] [PATCH installer 3/7] tui: install progress: split out reboot handling " Christoph Heiss @ 2023-07-26 14:03 ` Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 5/7] tui: install progress: handle errors in ui message loop more gracefully Christoph Heiss ` (2 subsequent siblings) 6 siblings, 0 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:03 UTC (permalink / raw) To: pve-devel No functional changes. Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- .../src/views/install_progress.rs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/proxmox-tui-installer/src/views/install_progress.rs b/proxmox-tui-installer/src/views/install_progress.rs index bd27628..3046a26 100644 --- a/proxmox-tui-installer/src/views/install_progress.rs +++ b/proxmox-tui-installer/src/views/install_progress.rs @@ -80,6 +80,27 @@ fn spawn_low_level_installer() -> io::Result<process::Child> { .spawn() } +fn show_prompt<W: Write + 'static>(siv: &mut Cursive, text: &str, writer: Arc<Mutex<W>>) { + yes_no_dialog( + siv, + "Prompt", + text, + Box::new({ + let writer = writer.clone(); + move |_| { + if let Ok(mut writer) = writer.lock() { + let _ = writeln!(writer, "ok"); + } + } + }), + Box::new(move |_| { + if let Ok(mut writer) = writer.lock() { + let _ = writeln!(writer); + } + }), + ); +} + fn prepare_for_reboot(siv: &mut Cursive, success: bool, msg: &str) { let title = if success { "Success" } else { "Failure" }; @@ -159,26 +180,7 @@ fn progress_task( })), UiMessage::Prompt(s) => cb_sink.send({ let writer = writer.clone(); - Box::new(move |siv| { - yes_no_dialog( - siv, - "Prompt", - &s, - Box::new({ - let writer = writer.clone(); - move |_| { - if let Ok(mut writer) = writer.lock() { - let _ = writeln!(writer, "ok"); - } - } - }), - Box::new(move |_| { - if let Ok(mut writer) = writer.lock() { - let _ = writeln!(writer); - } - }), - ); - }) + Box::new(move |siv| show_prompt(siv, &s, writer)) }), UiMessage::Progress(ratio, s) => { counter.set(ratio); -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH installer 5/7] tui: install progress: handle errors in ui message loop more gracefully 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss ` (3 preceding siblings ...) 2023-07-26 14:03 ` [pve-devel] [PATCH installer 4/7] tui: install progress: split out prompt logic " Christoph Heiss @ 2023-07-26 14:03 ` Christoph Heiss 2023-07-26 14:04 ` [pve-devel] [PATCH installer 6/7] low-level: avoid open-coding config reading, parsing and merging Christoph Heiss 2023-07-26 14:04 ` [pve-devel] [PATCH installer 7/7] low-level, tui: count down auto-reboot timeout Christoph Heiss 6 siblings, 0 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:03 UTC (permalink / raw) To: pve-devel This at least gives /some/ feedback to the user he can potentially report, instead of a single, hardcoded message. Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- .../src/views/install_progress.rs | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/proxmox-tui-installer/src/views/install_progress.rs b/proxmox-tui-installer/src/views/install_progress.rs index 3046a26..2025a4c 100644 --- a/proxmox-tui-installer/src/views/install_progress.rs +++ b/proxmox-tui-installer/src/views/install_progress.rs @@ -149,29 +149,36 @@ fn progress_task( }; let inner = || { - let reader = child.stdout.take().map(BufReader::new)?; - let mut writer = child.stdin.take()?; + let reader = child + .stdout + .take() + .map(BufReader::new) + .ok_or("failed to get stdin reader")?; - serde_json::to_writer(&mut writer, &InstallConfig::from(options)).unwrap(); - writeln!(writer).unwrap(); + let mut writer = child.stdin.take().ok_or("failed to get stdin writer")?; + + serde_json::to_writer(&mut writer, &InstallConfig::from(options)) + .map_err(|err| format!("failed to serialize install config: {err}"))?; + writeln!(writer).map_err(|err| format!("failed to write install config: {err}"))?; let writer = Arc::new(Mutex::new(writer)); for line in reader.lines() { let line = match line { Ok(line) => line, - Err(_) => break, + Err(err) => return Err(format!("low-level installer exited early: {err}")), }; let msg = match line.parse::<UiMessage>() { Ok(msg) => msg, Err(stray) => { + // Not a fatal error, so don't abort the installation by returning eprintln!("low-level installer: {stray}"); continue; } }; - match msg { + let result = match msg.clone() { UiMessage::Info(s) => cb_sink.send(Box::new(|siv| { siv.add_layer(Dialog::info(s).title("Information")); })), @@ -192,18 +199,23 @@ fn progress_task( progress_text.set_content(msg.to_owned()); cb_sink.send(Box::new(move |siv| prepare_for_reboot(siv, success, &msg))) } + }; + + if let Err(err) = result { + eprintln!("error during message handling: {err}"); + eprintln!(" message was: '{msg:?}'"); } - .unwrap(); } - Some(()) + Ok(()) }; - if inner().is_none() { + if let Err(err) = inner() { + let message = format!("install failed: {err}"); cb_sink .send(Box::new(|siv| { siv.add_layer( - Dialog::text("low-level installer exited early") + Dialog::text(message) .title("Error") .button("Exit", Cursive::quit), ); @@ -216,6 +228,7 @@ impl ViewWrapper for InstallProgressView { cursive::wrap_impl!(self.view: PaddedView<LinearLayout>); } +#[derive(Debug, Clone)] enum UiMessage { Info(String), Error(String), -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH installer 6/7] low-level: avoid open-coding config reading, parsing and merging 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss ` (4 preceding siblings ...) 2023-07-26 14:03 ` [pve-devel] [PATCH installer 5/7] tui: install progress: handle errors in ui message loop more gracefully Christoph Heiss @ 2023-07-26 14:04 ` Christoph Heiss 2023-07-26 14:04 ` [pve-devel] [PATCH installer 7/7] low-level, tui: count down auto-reboot timeout Christoph Heiss 6 siblings, 0 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:04 UTC (permalink / raw) To: pve-devel Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- proxmox-low-level-installer | 45 +++++++++++++++---------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/proxmox-low-level-installer b/proxmox-low-level-installer index 814961e..745402a 100755 --- a/proxmox-low-level-installer +++ b/proxmox-low-level-installer @@ -53,6 +53,22 @@ sub usage { exit($cmd ne 'help' ? 1 : 0); } +sub read_and_merge_config { + my $config_raw; + while (my $line = <>) { + if ($line =~ /^\s*\{/) { + $config_raw = $line; + last; + } + } + + my $config = eval { from_json($config_raw, { utf8 => 1 }) }; + die "failed to parse config from stdin - $@\n" if $@; + + Proxmox::Install::Config::merge($config); + log_info("got installation config: ". to_json(Proxmox::Install::Config::get(), { utf8 => 1, canonical => 1 }) ."\n"); +} + my $cmd = shift; if (!$cmd || $cmd eq 'help' || !exists($commands->{$cmd})) { usage($cmd // ''); @@ -87,19 +103,7 @@ if ($cmd eq 'dump-env') { file_write_all($run_env_file, $run_env_serialized); } elsif ($cmd eq 'start-session') { Proxmox::UI::init_stdio({}, $env); - - my $config_raw; - while (my $line = <>) { - if ($line =~ /^\s*\{/) { - $config_raw = $line; - last; - } - } - my $config = eval { from_json($config_raw, { utf8 => 1 }) }; - die "failed to parse config from stdin - $@\n" if $@; - - Proxmox::Install::Config::merge($config); - log_info("got installation config: ". to_json(Proxmox::Install::Config::get(), { utf8 => 1, canonical => 1 }) ."\n"); + read_and_merge_config(); eval { Proxmox::Install::extract_data() }; if (my $err = $@) { @@ -119,20 +123,7 @@ if ($cmd eq 'dump-env') { } } elsif ($cmd eq 'start-session-test') { Proxmox::UI::init_stdio({}, $env); - - my $config_raw; - while (my $line = <>) { - if ($line =~ /^\s*\{/) { - $config_raw = $line; - last; - } - } - my $config = eval { from_json($config_raw, { utf8 => 1 }) }; - die "failed to parse config from stdin - $@\n" if $@; - - Proxmox::Install::Config::merge($config); - - print STDERR "got config: ". to_json(Proxmox::Install::Config::get(), { utf8 => 1, canonical => 1 }) ."\n"; + read_and_merge_config(); my $res = Proxmox::UI::prompt("Reply anything?") ? 'ok' : 'not ok'; Proxmox::UI::message("Test Message - got $res"); -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [pve-devel] [PATCH installer 7/7] low-level, tui: count down auto-reboot timeout 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss ` (5 preceding siblings ...) 2023-07-26 14:04 ` [pve-devel] [PATCH installer 6/7] low-level: avoid open-coding config reading, parsing and merging Christoph Heiss @ 2023-07-26 14:04 ` Christoph Heiss 6 siblings, 0 replies; 8+ messages in thread From: Christoph Heiss @ 2023-07-26 14:04 UTC (permalink / raw) To: pve-devel The GUI installer already has the same functionality, with this the TUI installer gains the same. It is a nice touch anyway, primarily to indicate to the user that the installer is not frozen or similar. Signed-off-by: Christoph Heiss <c.heiss@proxmox.com> --- proxmox-low-level-installer | 25 +++++++++++-------- .../src/views/install_progress.rs | 12 +++++++-- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/proxmox-low-level-installer b/proxmox-low-level-installer index 745402a..0141170 100755 --- a/proxmox-low-level-installer +++ b/proxmox-low-level-installer @@ -69,6 +69,19 @@ sub read_and_merge_config { log_info("got installation config: ". to_json(Proxmox::Install::Config::get(), { utf8 => 1, canonical => 1 }) ."\n"); } +sub send_reboot_ui_message { + if (Proxmox::Install::Config::get_autoreboot()) { + my $secs = 5; + while ($secs > 0) { + Proxmox::UI::finished(1, "Installation finished - auto-rebooting in $secs seconds .."); + sleep 1; + $secs -= 1; + } + } else { + Proxmox::UI::finished(1, "Installation complete - reboot now?"); + } +} + my $cmd = shift; if (!$cmd || $cmd eq 'help' || !exists($commands->{$cmd})) { usage($cmd // ''); @@ -115,11 +128,7 @@ if ($cmd eq 'dump-env') { Proxmox::UI::finished(0, $err); } } else { - if (Proxmox::Install::Config::get_autoreboot()) { - Proxmox::UI::finished(1, "Installation finished - auto-rebooting in ~ 5 seconds"); - } else { - Proxmox::UI::finished(1, "Installation complete - reboot now?"); - } + send_reboot_ui_message(); } } elsif ($cmd eq 'start-session-test') { Proxmox::UI::init_stdio({}, $env); @@ -137,11 +146,7 @@ if ($cmd eq 'dump-env') { } } - if (Proxmox::Install::Config::get_autoreboot()) { - Proxmox::UI::finished(1, "Installation finished - auto-rebooting in ~ 5 seconds"); - } else { - Proxmox::UI::finished(1, "Installation complete - reboot now?"); - } + send_reboot_ui_message(); } exit(0); diff --git a/proxmox-tui-installer/src/views/install_progress.rs b/proxmox-tui-installer/src/views/install_progress.rs index 2025a4c..c8d9aab 100644 --- a/proxmox-tui-installer/src/views/install_progress.rs +++ b/proxmox-tui-installer/src/views/install_progress.rs @@ -9,7 +9,7 @@ use std::{ use cursive::{ utils::Counter, - view::{Resizable, ViewWrapper}, + view::{Nameable, Resizable, ViewWrapper}, views::{Dialog, DummyView, LinearLayout, PaddedView, ProgressBar, TextContent, TextView}, CbSink, Cursive, }; @@ -102,13 +102,21 @@ fn show_prompt<W: Write + 'static>(siv: &mut Cursive, text: &str, writer: Arc<Mu } fn prepare_for_reboot(siv: &mut Cursive, success: bool, msg: &str) { + const DIALOG_ID: &str = "autoreboot-dialog"; let title = if success { "Success" } else { "Failure" }; + // If the dialog was previously created, just update its content and we're done. + if let Some(mut dialog) = siv.find_name::<Dialog>(DIALOG_ID) { + dialog.set_content(TextView::new(msg)); + return; + } + // For rebooting, we just need to quit the installer, our caller does the actual reboot. siv.add_layer( Dialog::text(msg) .title(title) - .button("Reboot now", Cursive::quit), + .button("Reboot now", Cursive::quit) + .with_name(DIALOG_ID), ); let autoreboot = siv -- 2.41.0 ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2023-07-26 14:04 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-07-26 14:03 [pve-devel] [PATCH installer 0/7] refactor; improve installation progress Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 1/7] tui: move install progress dialog into own view module Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 2/7] tui: install progress: split out low-level installer spawing into own function Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 3/7] tui: install progress: split out reboot handling " Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 4/7] tui: install progress: split out prompt logic " Christoph Heiss 2023-07-26 14:03 ` [pve-devel] [PATCH installer 5/7] tui: install progress: handle errors in ui message loop more gracefully Christoph Heiss 2023-07-26 14:04 ` [pve-devel] [PATCH installer 6/7] low-level: avoid open-coding config reading, parsing and merging Christoph Heiss 2023-07-26 14:04 ` [pve-devel] [PATCH installer 7/7] low-level, tui: count down auto-reboot timeout Christoph Heiss
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox