-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix SetDPC request and implement SetServer request and UI #3
Conversation
rucoder
commented
Oct 31, 2024
- Add NTP server to IpDialog
- handle IpNet correctly
- Update to the latest ratatui/crossterm
- Implement better panic handling
- Disable some keystrokes in release mode e.g CTRL+a to trigger panic
- many more
raw-model is really redundant and has to be removed Also remove dummy set_dpc function in preparation for real implementation Signed-off-by: Mikhail Malyshev <[email protected]>
Signed-off-by: Mikhail Malyshev <[email protected]>
- Also fix some tests. NOTE: not all tests are passed because test data has to be updated Signed-off-by: Mikhail Malyshev <[email protected]>
Signed-off-by: Mikhail Malyshev <[email protected]>
Signed-off-by: Mikhail Malyshev <[email protected]>
- Linux assigns a Node Local IPv6 to every interface. They are not interesting for end users so filter them out - fix a bug with IPs not displayed if only a single IP is assigned Signed-off-by: Mikhail Malyshev <[email protected]>
- Move IpDialog state into a separate struct - Save initial state to compare later and make decision whether we need to change settings or not Signed-off-by: Mikhail Malyshev <[email protected]>
- use subnet instead of hard-coded values - use NtpServer instead of NtpServers for static IP configuration Signed-off-by: Mikhail Malyshev <[email protected]>
- add human friendli panic printing in debug mode - add panic logging in release mode - rework TerminalWrapper to reset terminam settings on panic Signed-off-by: Mikhail Malyshev <[email protected]>
Signed-off-by: Mikhail Malyshev <[email protected]>
- it was used for debugging debug Signed-off-by: Mikhail Malyshev <[email protected]>
- ratatui is a false-positive since it is a package name Signed-off-by: Mikhail Malyshev <[email protected]>
- use serde_as to simplify deserialization. Especially IpNet from GO - get rid of manually written tests and load real json files from the device Signed-off-by: Mikhail Malyshev <[email protected]>
- also pass old and new state to understand the difference and make a decision Signed-off-by: Mikhail Malyshev <[email protected]>
- Called on CTRL+s - TODO: prompt user to reboot the device if it was already onboarded Signed-off-by: Mikhail Malyshev <[email protected]>
Signed-off-by: Mikhail Malyshev <[email protected]>
- it is better to use constants than magic numbers Signed-off-by: Mikhail Malyshev <[email protected]>
529d673
to
8e6c46a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Big PR, will need a second time to go through it, do I get it right, that some tests were removed completely?
@@ -38,7 +41,8 @@ pub struct Application { | |||
ui: Ui, | |||
// this is our model :) | |||
model: Rc<Model>, | |||
raw_model: RawModel, | |||
// pending requests | |||
pending_requests: HashMap<u64, Rc<dyn Fn(&mut Application)>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rc, because you're not sharing this via threads, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because dyn
object doesn't have size. Rc
does. you need something to wrap your closure into. Since we do not pass across threads Rc
is enough
debug!("Pending response for: {:?}", request); | ||
self.pending_requests.insert(*id, Rc::new(handle_response)); | ||
} | ||
_ => {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to send any message here through the channel or just Request one? Or you just want to put Requests inside pending_requests? If former then you need to print error and return here, if latter -- you might want to use
if let IpcMessage::Request(request, id) = msg {
...
}
src/application.rs
Outdated
}, | ||
); | ||
} | ||
_ => {} // do nothing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print log message that there's some weird combination that is not handled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, makes sense. In theory we could have DHCP->DHCP transition with manual DNS for example but it is not supported in UI anyways
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I'd say better lay hay for future self in this case, things gets forgotten :) And in this case this won't add any overhead, neither computational, nor readability
} | ||
_ => {} // do nothing | ||
} | ||
self.send_ipc_message(IpcMessage::new_request(Request::SetDPC(new_dpc)), |_| {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so if nothing changes you still want to send message with "new" dpc, which hasn't been changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, this function is called here
UiActions::AppAction(app_action) => match app_action {
MonActions::NetworkInterfaceUpdated(old, new) => {
debug!("Setting DPC for {}", &old.iface_name);
debug!("OLD DPC: {:#?}", &old);
debug!("NEW DPC: {:#?}", &new);
if old == new {
debug!("Not changed, not sending DPC");
} else {
self.send_dpc(old, new);
}
self.ui.pop_layer();
}
MonActions::ServerUpdated(url) => {
debug!("Setting server URL to: {}", &url);
self.send_ipc_message(
IpcMessage::new_request(Request::SetServer(url.clone())),
move |app| {
app.model.borrow_mut().node_status.server = Some(url.clone());
},
);
self.ui.pop_layer();
}
},
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, missed that, you handle it there, makes sense
@@ -363,6 +461,7 @@ impl Application { | |||
// send initial redraw event | |||
self.invalidate(); | |||
|
|||
#[allow(unused_assignments)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
read the comment above.
@@ -50,6 +50,54 @@ fn init_logging() -> log2::Handle { | |||
handle | |||
} | |||
|
|||
pub fn initialize_panic_handler() -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 👏 👏
panic handlers are good thing
@@ -173,7 +173,7 @@ impl IPresenter for ApplicationsPage { | |||
area: &ratatui::prelude::Rect, | |||
frame: &mut ratatui::Frame<'_>, | |||
model: &std::rc::Rc<Model>, | |||
focused: bool, | |||
_focused: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why keep it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because it is used in other implementation of this trait but not in this one
@@ -90,11 +90,13 @@ impl<D> WindowBuilder<D> { | |||
self | |||
} | |||
|
|||
#[allow(dead_code)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you want to keep them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is an API of UI widgets. I have plans for them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, gotcha
I guess we should ask @jsfakian to take a look. At least he knows how to read rust code... |
I will do my best. |
hint: String, | ||
} | ||
|
||
fn on_init(w: &mut Window<InputDialogState>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think that it makes sense to make input dialog as a trait? Say you want to input ip addr which has some checks to be valid, mac addr, which also has some other checks, etc. etc. It's more about near future implementations rather than current PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@uncleDecart if that would be so simple... PRs are always welcome ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, some questions which are more discussions and one fix with printing error.
- in theory crossterm shouldn't depend on libc but there is a bug when crossterm cannot be built with event-stream and dev-tty without libc Signed-off-by: Mikhail Malyshev <[email protected]>
- we need to update some fields in the model only if our request was successful - on error we can show a message to the user Signed-off-by: Mikhail Malyshev <[email protected]>
- this is a simple dialog with Label that can show a message to the user - do not allow changing of server URL if the device is onboarded Signed-off-by: Mikhail Malyshev <[email protected]>
- We do not want user to accidentally cause a panic or exit the application - Do not strip symbol names from release build to see readable stack trace Signed-off-by: Mikhail Malyshev <[email protected]>
Signed-off-by: Mikhail Malyshev <[email protected]>
- this tab is useful for debugging only - cleanup some warnings Signed-off-by: Mikhail Malyshev <[email protected]>
8e6c46a
to
2a407bf
Compare
- Such transition may be valid if we ever support DHCP with e.g. static DNS. For now log an error Signed-off-by: Mikhail Malyshev <[email protected]>
debug!("Got response: {:?}", result); | ||
match result { | ||
Ok(_) => { | ||
debug!("Response OK"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does debug! macros adds function name? Would be useful to have it IMO
@@ -48,6 +50,7 @@ impl Application { | |||
let terminal = TerminalWrapper::open_terminal()?; | |||
let mut ui = Ui::new(action_tx.clone(), terminal)?; | |||
let model = Rc::new(RefCell::new(MonitorModel::default())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why Rc<RefCell>
can't it be just Rc<MonitorModel>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@uncleDecart no, we need interior mutability here or we cannot pass model to other functions because 'it is already borrowed as mut'.
@@ -137,20 +174,85 @@ impl Application { | |||
self.model.borrow_mut().update_app_list(app_list); | |||
} | |||
|
|||
IpcMessage::ZedAgentStatus(status) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't comment on code above, but there's IpcMessage::LedBlinkCounter(led) case which just prints that we have LedBlinkCounter, is that intended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you want to keep variable you can just write _led to silent compiler warning
src/application.rs
Outdated
let current_dpc = self.model.borrow().get_current_dpc().cloned(); | ||
if let Some(current_dpc) = current_dpc { | ||
info!("send_dpc: Sending DPC for iface {}", &new.iface_name); | ||
let dpc = current_dpc.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you clone current_dpc twice here, once
let current_dpc = self.model.borrow().get_current_dpc().cloned();
once here, doesn't make sense to clone twice, maybe even to clone at all actually
@@ -310,7 +412,7 @@ impl Application { | |||
} | |||
|
|||
fn create_terminal_task(&mut self) -> (JoinHandle<()>, CancellationToken) { | |||
let mut terminal_event_stream = self.ui.terminal.get_stream(); | |||
let mut terminal_event_stream = TerminalWrapper::get_stream(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so now stream is singleton?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, it always was de-facto. the ownership of the stream is pretty complex here but the real underlining stream is a singleton in crossterm. I can explain offline if you wish, very long explanation
src/application.rs
Outdated
UiActions::ChangeServer => { | ||
let is_onboarded = self.model.borrow().node_status.is_onboarded(); | ||
|
||
if is_onboarded { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're not using the variable here, why not
if self.model.borrow().node_status.is_onboarded() {
// ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no specific reason really. I can change it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no reason :) i'll change
self.send_ipc_message( | ||
IpcMessage::new_request(Request::SetServer(url.clone())), | ||
move |app| { | ||
app.model.borrow_mut().node_status.server = Some(url.clone()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clone is not needed, it's the last place where you use url variable in this branch
src/model/model.rs
Outdated
@@ -182,6 +192,7 @@ impl MonitorModel { | |||
} | |||
|
|||
pub fn update_network_status(&mut self, net_status: DeviceNetworkStatus) { | |||
self.dpc_key = Some(net_status.dpc_key.clone()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why clone dpc_key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, the answer to "why clone()..." question in 90% of cases is "to satisfy borrow checker". But this is a really good catch! I rewrote it without cloning.
@@ -289,7 +324,7 @@ fn update_current_layout(w: &mut Window<IpDialogState>, rect: &Rect) { | |||
} | |||
} | |||
|
|||
fn ip_dialog_layout(w: &mut Window<IpDialogState>, rect: &Rect, model: &Rc<Model>) { | |||
fn ip_dialog_layout(w: &mut Window<IpDialogState>, rect: &Rect, _model: &Rc<Model>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a trait, right? Why keep model as a parameter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because it is a function which is used as closure later. in .with_layout(....) call
.collect::<Vec<_>>() | ||
.join("\n"); | ||
|
||
// cell #3 IP address list | ||
if height > 1 { | ||
if height > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not that good with the code base (yet :D) why 0 instead of 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a real bug here: if we have a single IP (height 1) we display ... nothing :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, some small comments removing unnecessary cloning and clarifications about code base :)
- remove two unnecessary clone() calls - remove verbose log Signed-off-by: Mikhail Malyshev <[email protected]>