Test against mock rpc server

This commit is contained in:
dav 2024-08-16 17:32:18 -07:00 committed by David Zhong
parent fec725bc52
commit c455c1fbbe
7 changed files with 82 additions and 289 deletions

View file

@ -2,7 +2,7 @@ use super::*;
use anyhow::Context; use anyhow::Context;
use std::time::Duration; use std::time::Duration;
const RECONNECT_POLL_TIME: Duration = Duration::from_secs(5); const RECONNECT_POLL_TIME: Duration = Duration::from_secs(3);
pub struct App { pub struct App {
daemon_client: Arc<Mutex<Option<Channel>>>, daemon_client: Arc<Mutex<Option<Channel>>>,
@ -58,7 +58,16 @@ impl AsyncComponent for App {
root: Self::Root, root: Self::Root,
sender: AsyncComponentSender<Self>, sender: AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> { ) -> AsyncComponentParts<Self> {
let daemon_client = Arc::new(Mutex::new(daemon::daemon_connect().await.ok())); // TODO: RPC REFACTOR (Handle Error)
let mut daemon_client_connected = false;
let daemon_client = Arc::new(Mutex::new(
daemon::daemon_connect()
.await
.inspect(|_| {
daemon_client_connected = true;
})
.ok(),
));
let main_screen = main_screen::MainScreen::builder() let main_screen = main_screen::MainScreen::builder()
.launch(main_screen::MainScreenInit { .launch(main_screen::MainScreenInit {
@ -72,6 +81,17 @@ impl AsyncComponent for App {
}) })
.forward(sender.input_sender(), |_| AppMsg::None); .forward(sender.input_sender(), |_| AppMsg::None);
if !daemon_client_connected {
main_screen
.sender()
.send(main_screen::MainScreenMsg::DaemonDisconnect)
.unwrap();
settings_screen
.sender()
.send(settings_screen::SettingsScreenMsg::DaemonStateChange)
.unwrap();
}
let widgets = view_output!(); let widgets = view_output!();
let view_stack = adw::ViewStack::new(); let view_stack = adw::ViewStack::new();
@ -126,16 +146,21 @@ impl AsyncComponent for App {
let mut daemon_client = self.daemon_client.lock().await; let mut daemon_client = self.daemon_client.lock().await;
let mut disconnected_daemon_client = false; let mut disconnected_daemon_client = false;
if let Some(daemon_client) = daemon_client.as_mut() { if let Some(client) = daemon_client.as_mut() {
let mut client = tunnel_client::TunnelClient::new(daemon_client); let mut client = tunnel_client::TunnelClient::new(client);
if let Err(_e) = client.tunnel_status(burrow_rpc::Empty {}).await {
if let Ok(mut res) = client.tunnel_status(burrow_rpc::Empty {}).await {
let stream = res.get_mut();
while let Ok(Some(_)) = stream.message().await {}
}
*daemon_client = None;
disconnected_daemon_client = true; disconnected_daemon_client = true;
self.main_screen self.main_screen
.emit(main_screen::MainScreenMsg::DaemonDisconnect); .emit(main_screen::MainScreenMsg::DaemonDisconnect);
self.settings_screen self.settings_screen
.emit(settings_screen::SettingsScreenMsg::DaemonStateChange) .emit(settings_screen::SettingsScreenMsg::DaemonStateChange)
} }
}
if disconnected_daemon_client || daemon_client.is_none() { if disconnected_daemon_client || daemon_client.is_none() {
match daemon::daemon_connect().await { match daemon::daemon_connect().await {

View file

@ -6,4 +6,4 @@ mod switch;
pub use network_card::{NetworkCard, NetworkCardInit}; pub use network_card::{NetworkCard, NetworkCardInit};
pub use networks::{Networks, NetworksInit}; pub use networks::{Networks, NetworksInit};
pub use switch::{Switch, SwitchInit, SwitchMsg}; pub use switch::{Switch, SwitchInit};

View file

@ -86,30 +86,28 @@ impl AsyncComponent for NetworkCard {
async fn update( async fn update(
&mut self, &mut self,
msg: Self::Input, msg: Self::Input,
sender: AsyncComponentSender<Self>, _: AsyncComponentSender<Self>,
_root: &Self::Root, _root: &Self::Root,
) { ) {
match msg { match msg {
NetworkCardMsg::NetworkDelete => { NetworkCardMsg::NetworkDelete => {
if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() { if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() {
let mut client = networks_client::NetworksClient::new(daemon_client); let mut client = networks_client::NetworksClient::new(daemon_client);
client let _ = client
.network_delete(burrow_rpc::NetworkDeleteRequest { id: self.id }) .network_delete(burrow_rpc::NetworkDeleteRequest { id: self.id })
.await .await;
.unwrap();
} }
} }
NetworkCardMsg::MoveUp => { NetworkCardMsg::MoveUp => {
if self.index.checked_sub(1).is_some() { if self.index.checked_sub(1).is_some() {
if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() { if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() {
let mut client = networks_client::NetworksClient::new(daemon_client); let mut client = networks_client::NetworksClient::new(daemon_client);
client let _ = client
.network_reorder(burrow_rpc::NetworkReorderRequest { .network_reorder(burrow_rpc::NetworkReorderRequest {
id: self.id, id: self.id,
index: self.index as i32 - 1, index: self.index as i32 - 1,
}) })
.await .await;
.unwrap();
} }
} }
} }
@ -117,13 +115,12 @@ impl AsyncComponent for NetworkCard {
if self.index + 1 < self.index_max { if self.index + 1 < self.index_max {
if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() { if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() {
let mut client = networks_client::NetworksClient::new(daemon_client); let mut client = networks_client::NetworksClient::new(daemon_client);
client let _ = client
.network_reorder(burrow_rpc::NetworkReorderRequest { .network_reorder(burrow_rpc::NetworkReorderRequest {
id: self.id, id: self.id,
index: self.index as i32 + 1, index: self.index as i32 + 1,
}) })
.await .await;
.unwrap();
} }
} }
} }

View file

@ -7,6 +7,8 @@ pub struct Networks {
daemon_client: Arc<Mutex<Option<Channel>>>, daemon_client: Arc<Mutex<Option<Channel>>>,
network_cards: Vec<AsyncController<NetworkCard>>, network_cards: Vec<AsyncController<NetworkCard>>,
networks_list_box: gtk::ListBox, networks_list_box: gtk::ListBox,
_network_state_worker: WorkerController<AsyncNetworkStateHandler>,
} }
pub struct NetworksInit { pub struct NetworksInit {
@ -94,6 +96,10 @@ impl AsyncComponent for Networks {
daemon_client: init.daemon_client, daemon_client: init.daemon_client,
network_cards, network_cards,
networks_list_box: widgets.networks.clone(), networks_list_box: widgets.networks.clone(),
_network_state_worker: AsyncNetworkStateHandler::builder()
.detach_worker(())
.forward(sender.input_sender(), |msg| msg),
}; };
AsyncComponentParts { model, widgets } AsyncComponentParts { model, widgets }
@ -132,7 +138,7 @@ impl AsyncComponent for Networks {
NetworksMsg::NetworkAdd => { NetworksMsg::NetworkAdd => {
if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() { if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() {
let mut client = networks_client::NetworksClient::new(daemon_client); let mut client = networks_client::NetworksClient::new(daemon_client);
client.network_add(burrow_rpc::Empty {}).await.unwrap(); let _ = client.network_add(burrow_rpc::Empty {}).await;
} }
} }
_ => {} _ => {}
@ -147,7 +153,8 @@ impl Worker for AsyncNetworkStateHandler {
type Input = (); type Input = ();
type Output = NetworksMsg; type Output = NetworksMsg;
fn init(_: Self::Init, _sender: ComponentSender<Self>) -> Self { fn init(_: Self::Init, sender: ComponentSender<Self>) -> Self {
sender.input(());
Self Self
} }

View file

@ -6,8 +6,6 @@ const RECONNECT_POLL_TIME: Duration = Duration::from_secs(3);
pub struct Switch { pub struct Switch {
daemon_client: Arc<Mutex<Option<Channel>>>, daemon_client: Arc<Mutex<Option<Channel>>>,
switch: gtk::Switch, switch: gtk::Switch,
switch_screen: gtk::Box,
disconnected_banner: adw::Banner,
_tunnel_state_worker: WorkerController<AsyncTunnelStateHandler>, _tunnel_state_worker: WorkerController<AsyncTunnelStateHandler>,
} }
@ -19,8 +17,6 @@ pub struct SwitchInit {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum SwitchMsg { pub enum SwitchMsg {
None, None,
DaemonReconnect,
DaemonDisconnect,
Start, Start,
Stop, Stop,
SwitchSetStart, SwitchSetStart,
@ -51,7 +47,6 @@ impl AsyncComponent for Switch {
}, },
}, },
#[name(switch_screen)]
gtk::Box { gtk::Box {
set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
set_spacing: 10, set_spacing: 10,
@ -80,37 +75,12 @@ impl AsyncComponent for Switch {
root: Self::Root, root: Self::Root,
sender: AsyncComponentSender<Self>, sender: AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> { ) -> AsyncComponentParts<Self> {
let mut initial_daemon_server_down = false;
if let Some(daemon_client) = init.daemon_client.lock().await.as_mut() {
let mut client = tunnel_client::TunnelClient::new(daemon_client);
if client
.tunnel_status(burrow_rpc::Empty {})
.await
.as_mut()
.is_err()
{
initial_daemon_server_down = true;
}
} else {
initial_daemon_server_down = true;
}
let switch_sender = sender.clone(); let switch_sender = sender.clone();
let widgets = view_output!(); let widgets = view_output!();
if initial_daemon_server_down {
*init.daemon_client.lock().await = None;
widgets.switch.set_active(false);
widgets.switch_screen.set_sensitive(false);
widgets.setup_banner.set_revealed(true);
}
let model = Switch { let model = Switch {
daemon_client: init.daemon_client, daemon_client: init.daemon_client,
switch: widgets.switch.clone(), switch: widgets.switch.clone(),
switch_screen: widgets.switch_screen.clone(),
disconnected_banner: widgets.setup_banner.clone(),
_tunnel_state_worker: AsyncTunnelStateHandler::builder() _tunnel_state_worker: AsyncTunnelStateHandler::builder()
.detach_worker(()) .detach_worker(())
.forward(sender.input_sender(), |_| SwitchMsg::None), .forward(sender.input_sender(), |_| SwitchMsg::None),
@ -127,20 +97,16 @@ impl AsyncComponent for Switch {
_: AsyncComponentSender<Self>, _: AsyncComponentSender<Self>,
_root: &Self::Root, _root: &Self::Root,
) { ) {
let mut disconnected_daemon_client = false;
if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() { if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() {
let mut client = tunnel_client::TunnelClient::new(daemon_client); let mut client = tunnel_client::TunnelClient::new(daemon_client);
match msg { match msg {
Self::Input::Start => { Self::Input::Start => {
if let Err(_e) = client.tunnel_start(burrow_rpc::Empty {}).await { // TODO: Figure out best way for error handling.
disconnected_daemon_client = true; let _ = client.tunnel_start(burrow_rpc::Empty {}).await;
}
} }
Self::Input::Stop => { Self::Input::Stop => {
if let Err(_e) = client.tunnel_stop(burrow_rpc::Empty {}).await { // TODO: Figure out best way for error handling.
disconnected_daemon_client = true; let _ = client.tunnel_stop(burrow_rpc::Empty {}).await;
}
} }
Self::Input::SwitchSetStart => { Self::Input::SwitchSetStart => {
self.switch.set_active(true); self.switch.set_active(true);
@ -150,19 +116,6 @@ impl AsyncComponent for Switch {
} }
_ => {} _ => {}
} }
} else {
disconnected_daemon_client = true;
}
if msg == Self::Input::DaemonReconnect {
self.disconnected_banner.set_revealed(false);
self.switch_screen.set_sensitive(true);
}
if disconnected_daemon_client || msg == Self::Input::DaemonDisconnect {
*self.daemon_client.lock().await = None;
self.switch_screen.set_sensitive(false);
self.disconnected_banner.set_revealed(true);
} }
} }
} }
@ -174,7 +127,8 @@ impl Worker for AsyncTunnelStateHandler {
type Input = (); type Input = ();
type Output = SwitchMsg; type Output = SwitchMsg;
fn init(_: Self::Init, _sender: ComponentSender<Self>) -> Self { fn init(_: Self::Init, sender: ComponentSender<Self>) -> Self {
sender.input(());
Self Self
} }

View file

@ -1,8 +1,10 @@
use super::*; use super::*;
pub struct MainScreen { pub struct MainScreen {
switch: AsyncController<main::Switch>, _switch: AsyncController<main::Switch>,
networks: AsyncController<main::Networks>, _networks: AsyncController<main::Networks>,
content_box: gtk::Box,
daemon_status_banner: adw::Banner,
} }
pub struct MainScreenInit { pub struct MainScreenInit {
@ -29,17 +31,17 @@ impl AsyncComponent for MainScreen {
set_valign: Align::Fill, set_valign: Align::Fill,
set_valign: Align::Center, set_valign: Align::Center,
// gtk::Box { gtk::Box {
// set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
// set_spacing: 5, set_spacing: 5,
// set_margin_all: 5, set_margin_all: 5,
// set_valign: Align::Start, set_valign: Align::Start,
// #[name(setup_banner)] #[name(daemon_status_banner)]
// adw::Banner { adw::Banner {
// set_title: "Burrow is not running!", set_title: "Burrow is not running!",
// }, },
// }, },
gtk::Box { gtk::Box {
set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
@ -86,7 +88,12 @@ impl AsyncComponent for MainScreen {
widgets.content.append(networks.widget()); widgets.content.append(networks.widget());
widgets.content.append(switch.widget()); widgets.content.append(switch.widget());
let model = MainScreen { switch, networks }; let model = MainScreen {
_switch: switch,
_networks: networks,
content_box: widgets.content.clone(),
daemon_status_banner: widgets.daemon_status_banner.clone(),
};
AsyncComponentParts { model, widgets } AsyncComponentParts { model, widgets }
} }
@ -99,10 +106,12 @@ impl AsyncComponent for MainScreen {
) { ) {
match msg { match msg {
MainScreenMsg::DaemonDisconnect => { MainScreenMsg::DaemonDisconnect => {
self.switch.emit(main::SwitchMsg::DaemonDisconnect); self.daemon_status_banner.set_revealed(true);
self.content_box.set_sensitive(false);
} }
MainScreenMsg::DaemonReconnect => { MainScreenMsg::DaemonReconnect => {
self.switch.emit(main::SwitchMsg::DaemonReconnect); self.daemon_status_banner.set_revealed(false);
self.content_box.set_sensitive(true);
} }
_ => {} _ => {}
} }

View file

@ -1,199 +0,0 @@
use super::*;
use std::{
thread::{self, JoinHandle},
time::Duration,
};
const RECONNECT_POLL_TIME: Duration = Duration::from_secs(3);
pub struct SwitchScreen {
daemon_client: Arc<Mutex<Option<Channel>>>,
switch: gtk::Switch,
switch_screen: gtk::Box,
disconnected_banner: adw::Banner,
tunnel_state_worker: JoinHandle<()>,
}
pub struct SwitchScreenInit {
pub daemon_client: Arc<Mutex<Option<Channel>>>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum SwitchScreenMsg {
DaemonReconnect,
DaemonDisconnect,
Start,
Stop,
TunnelStateStopped,
TunnelStateRunning,
}
#[relm4::component(pub, async)]
impl AsyncComponent for SwitchScreen {
type Init = SwitchScreenInit;
type Input = SwitchScreenMsg;
type Output = ();
type CommandOutput = SwitchScreenMsg;
view! {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_valign: Align::Fill,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
set_margin_all: 5,
set_valign: Align::Start,
#[name(setup_banner)]
adw::Banner {
set_title: "Burrow is not running!",
},
},
#[name(switch_screen)]
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 10,
set_margin_all: 5,
set_valign: Align::Center,
set_vexpand: true,
gtk::Label {
set_label: "Burrow Switch",
},
#[name(switch)]
gtk::Switch {
set_halign: Align::Center,
set_hexpand: false,
set_vexpand: false,
connect_active_notify => move |switch|
switch_sender.input(if switch.is_active() { SwitchScreenMsg::Start } else { SwitchScreenMsg::Stop })
},
}
}
}
async fn init(
init: Self::Init,
root: Self::Root,
sender: AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> {
let mut initial_daemon_server_down = false;
if let Some(daemon_client) = init.daemon_client.lock().await.as_mut() {
let mut client = tunnel_client::TunnelClient::new(daemon_client);
if client
.tunnel_status(burrow_rpc::Empty {})
.await
.as_mut()
.is_err()
{
initial_daemon_server_down = true;
}
} else {
initial_daemon_server_down = true;
}
let switch_sender = sender.clone();
let tunnel_state_sender = sender.clone();
let widgets = view_output!();
if initial_daemon_server_down {
*init.daemon_client.lock().await = None;
widgets.switch.set_active(false);
widgets.switch_screen.set_sensitive(false);
widgets.setup_banner.set_revealed(true);
}
let model = SwitchScreen {
daemon_client: init.daemon_client,
switch: widgets.switch.clone(),
switch_screen: widgets.switch_screen.clone(),
disconnected_banner: widgets.setup_banner.clone(),
tunnel_state_worker: thread::spawn(move || {
tunnel_state_worker(tunnel_state_sender);
}),
};
widgets.switch.set_active(false);
AsyncComponentParts { model, widgets }
}
async fn update(
&mut self,
msg: Self::Input,
_: AsyncComponentSender<Self>,
_root: &Self::Root,
) {
let mut disconnected_daemon_client = false;
if let Some(daemon_client) = self.daemon_client.lock().await.as_mut() {
let mut client = tunnel_client::TunnelClient::new(daemon_client);
match msg {
Self::Input::Start => {
if let Err(e) = client.tunnel_start(burrow_rpc::Empty {}).await {
disconnected_daemon_client = true;
}
}
Self::Input::Stop => {
if let Err(e) = client.tunnel_stop(burrow_rpc::Empty {}).await {
disconnected_daemon_client = true;
}
}
Self::Input::TunnelStateStopped => {
self.switch.set_active(false);
}
Self::Input::TunnelStateRunning => {
self.switch.set_active(true);
}
_ => {}
}
} else {
disconnected_daemon_client = true;
}
if msg == Self::Input::DaemonReconnect {
self.disconnected_banner.set_revealed(false);
self.switch_screen.set_sensitive(true);
}
if disconnected_daemon_client || msg == Self::Input::DaemonDisconnect {
*self.daemon_client.lock().await = None;
self.switch_screen.set_sensitive(false);
self.disconnected_banner.set_revealed(true);
}
}
}
fn tunnel_state_worker(sender: AsyncComponentSender<SwitchScreen>) {
let rt = tokio::runtime::Runtime::new().unwrap();
let task = rt.spawn(async move {
loop {
let sender = sender.input_sender();
let conn = daemon::daemon_connect().await;
if let Ok(conn) = conn {
let mut client = tunnel_client::TunnelClient::new(conn);
if let Ok(mut res) = client.tunnel_status(burrow_rpc::Empty {}).await {
let stream = res.get_mut();
while let Ok(Some(msg)) = stream.message().await {
match msg.state() {
burrow_rpc::State::Stopped => {
sender.send(SwitchScreenMsg::TunnelStateStopped)
}
burrow_rpc::State::Running => {
sender.send(SwitchScreenMsg::TunnelStateRunning)
}
}
.unwrap();
}
}
}
tokio::time::sleep(RECONNECT_POLL_TIME).await;
}
});
rt.block_on(task).unwrap();
}