* [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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal