-
Notifications
You must be signed in to change notification settings - Fork 6
feat: network settings #51
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
base: main
Are you sure you want to change the base?
feat: network settings #51
Conversation
- upgraded vuetify from ^3.8.12 to ^3.9.0 - updated @biomejs/biome from 2.0.6 to 2.1.1 - updated @types/bun from ^1.2.17 to ^1.2.18 - upgraded unocss from ^66.3.2 to ^66.3.3 - updated vite from ^7.0.0 to ^7.0.3 - upgraded vue-tsc from ^3.0.0 to ^3.0.1
…ix URL reference in healthcheck
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 overall minor nits
src/api.rs
Outdated
previous_ip: String, | ||
ip: Option<String>, | ||
netmask: Option<u8>, | ||
gateway: Option<Vec<String>>, | ||
dns: Option<Vec<String>>, |
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.
Preferably use strong types instead of "stringly types". The standard has your back here: std::net::IpAddr (+ std::net::Ipv4Addr, std::net::Ipv6Addr). serde
conveniently already provides Serialize/Deserialize implementations for these types.
The benefit is, that you gain some addtional validity checks + convenience methods for working with these types.
src/api.rs
Outdated
} | ||
|
||
pub async fn set_network( | ||
body: web::Json<NetworkSetting>, |
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.
nit: rather use "network_settings" than body. body communicates "this is the body of the request", whereas this already is the parsed body.
src/api.rs
Outdated
if body.name.is_empty() { | ||
return HttpResponse::BadRequest().body("Network name cannot be empty"); | ||
} |
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.
Maybe it would be sensible to add in something like: https://crates.io/crates/serde_valid
src/api.rs
Outdated
|
||
if let Some(ip) = &body.ip { | ||
if !ip.is_empty() && ip.parse::<std::net::IpAddr>().is_err() { | ||
return HttpResponse::BadRequest().body("Invalid IP address format"); | ||
} | ||
} | ||
|
||
if let Some(netmask) = &body.netmask { | ||
if *netmask > 32 { | ||
return HttpResponse::BadRequest().body("Netmask must be between 0 and 32"); | ||
} | ||
} | ||
|
||
if let Some(gateway) = &body.gateway { | ||
for gw in gateway { | ||
if !gw.is_empty() && gw.parse::<std::net::IpAddr>().is_err() { | ||
return HttpResponse::BadRequest().body("Invalid gateway format"); | ||
} | ||
} | ||
} | ||
|
||
if let Some(dns) = &body.dns { | ||
for dns_entry in dns { | ||
if !dns_entry.is_empty() && dns_entry.parse::<std::net::IpAddr>().is_err() { | ||
return HttpResponse::BadRequest().body("Invalid DNS format"); | ||
} | ||
} | ||
} |
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.
The same goes for these checks
src/api.rs
Outdated
if fs::exists(&config_file)? { | ||
fs::remove_file(&config_file)?; | ||
} | ||
|
||
if fs::exists(&backup_file)? { | ||
fs::rename(backup_file, config_file)?; | ||
} |
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.
Couple of things (but maybe I don't get the logic fully here):
- I'd copy the backup file instead of moving it, to keep it around, just in case.
- I'd remove the existing config file only if there is a backup file or do we have another level of fallback?
- Probably it is sensible to make the operation atomic, i.e. copy backup to config file directly?
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.
Another level of fallback would be the default network config in /lib/systemd/network/
) -> Result<()> { | ||
let _ = self.restore_network_setting(network.clone()); | ||
let _ = self.ods_client.reload_network().await; | ||
let _ = certificate::create_module_certificate(Some(network.previous_ip)).await; | ||
crate::trigger_server_restart(); | ||
|
||
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.
Why have this function return result if the calls don't propagate/handle the errors?
src/api.rs
Outdated
if Path::new(&backup_file).exists() { | ||
fs::remove_file(&backup_file).context("Unable to remove backup file")?; | ||
} | ||
|
||
if Path::new(&config_file).exists() { | ||
fs::rename(config_file, backup_file).context("Failed to back up file")?; | ||
} else { |
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.
File operations should always be performed atomically, ideally: try to do the operation that you want to perform and handle the unexpected case. In this case I'd do sth. like the following:
if let Err(ref e) = fs::remove_file("/tmp/some/file/path.txt") &&
e.kind() != ErrorKind::NotFound {
...
}
the same goes for renaming/copying the backup file.
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 do copy now instead of moving. So if it would fail to write new config, old config will be still available.
src/api.rs
Outdated
.as_secs() | ||
+ 90; // 90 seconds from now |
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.
... + std::Duration::from_seconds(5) ... is longer but IMHO more expressive regarding the intend.
src/api.rs
Outdated
#[derive(Serialize, Deserialize, Clone)] | ||
struct PendingNetworkRollback { | ||
network_setting: NetworkSetting, | ||
rollback_time: u64, |
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 I understand the code correctly, the rollback_time is only used by the omnect-ui to check since when there is a network rollback in progress? serde provides serialization/deserialization for std::time::Duration
, i.e., I think it would be best to make rollback_time a std::time::Duration
and let serde handle the rest. This would simplify the code downstream as you don't have to manually convert via seconds from u64 to duration and back.
src/main.rs
Outdated
|
||
pub fn trigger_server_restart() { | ||
if let Some(tx) = SERVER_RESTART_TX.get() { | ||
let _ = tx.send(()); |
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.
IMHO we should log any errors for later analysis.
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.
frontend seems ok for me :)
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.
As far as I understand the code there will never be a situation where we call trigger_server_restart
or cancel_rollback_timer
and they have not yet been initialized. Is there some test scenario that I overlook?
IMHO the if let Some(...)
clauses are then complicated and confusing (as I have to wonder: what if they are not initialized? in what cases are they not initialized?). I'd probably go away from global variables and make a ServerControl
struct that is part of API.
Both AbortHandle and broadcast::Sender implement Clone, i.e., their principle idea is, that instead of having a single instance that requires locking for synchornized access, they can be cloned cheaply and thus don't need synchronization (or, more correctly, take care of synchronization under the hood.).
src/api.rs
Outdated
HttpResponse::Ok().finish() | ||
} | ||
|
||
fn restore_network_setting(&self, network: NetworkSetting) -> 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.
is there a reason not to pass network by reference?
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.
Minor nits, but looks good to me overall :)
src/api.rs
Outdated
Ok(()) | ||
} | ||
|
||
async fn configure_network_interface(&self, network: NetworkSetting) -> 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.
can we pass here network by reference?
src/api.rs
Outdated
|
||
async fn handle_server_restart_with_new_certificate( | ||
&self, | ||
network: NetworkSetting, |
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.
pass network by reference
src/api.rs
Outdated
} | ||
|
||
pub async fn check_and_execute_pending_rollback(&self) -> Result<()> { | ||
if let Some(pending) = self.load_pending_rollback() { |
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.
IMHO its fine to return early here to reduce nesting, i.e., let Some(pending) = self.load_pending_rollback() else return 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.
agree
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 sure what the difference to the current implementation is supposed to be.
…and service client
- Bumped versions for @vueuse/core, axios, centrifuge, vue, and vuetify in dependencies. - Updated devDependencies: @biomejs/biome, @types/bun, @vitejs/plugin-vue, @vue/tsconfig, typescript, unocss, vite, vite-plugin-vuetify, and vue-tsc.
…roved performance
src/api.rs
Outdated
error!("Failed to save pending rollback: {e:#}"); | ||
} | ||
|
||
let network_clone = network.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.
please rename to let network = network.clone();
src/api.rs
Outdated
} | ||
|
||
pub async fn check_and_execute_pending_rollback(&self) -> Result<()> { | ||
if let Some(pending) = self.load_pending_rollback() { |
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.
agree
src/api.rs
Outdated
let config_file = network_path!(format!("10-{}.network", network.name)); | ||
let backup_file = network_path!(format!("10-{}.network.old", network.name)); | ||
|
||
if !fs::exists(&backup_file)? { |
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.
sure this is what you want? it might return with an error...
This pull request introduces several updates across multiple files, focusing on adding network configuration management, enhancing rollback capabilities, and updating dependencies. The changes include new functionality for managing network settings, improvements to error handling, and updates to configuration files and dependencies.
Network Configuration Management:
src/api.rs
: Added support for network configuration management, including functions to set network settings, handle server restarts with new certificates, and manage rollbacks for network changes. Introduced new types likeNetworkSetting
andPendingNetworkRollback
, and added macros for file paths. [1] [2] [3]src/main.rs
: Added global server restart signal and rollback timer management to facilitate network rollback and server restart workflows.Rollback Enhancements:
src/api.rs
: Implemented rollback mechanisms for network settings, including saving, loading, and executing pending rollbacks, as well as canceling rollback timers.src/main.rs
: Added utility functions for managing rollback timers, such asset_rollback_timer
andcancel_rollback_timer
.Dependency Updates:
Cargo.toml
: Added new dependencies likerust-ini
for INI file handling andactix-cors
for cross-origin resource sharing. Updated existing dependency versions. [1] [2]Configuration Updates:
biome.json
: Upgraded schema version and added new linter rules for stricter code style enforcement. Updated file inclusion patterns. [1] [2] [3]Miscellaneous Improvements:
src/certificate.rs
: Modifiedcreate_module_certificate
to accept an optional IP address for certificate generation, enabling dynamic updates based on network changes. [1] [2]build-and-run-image.sh
: Added volume mapping for thenetwork
directory to support network configuration changes in Docker containers.