Added ssh functionality and improved hostnames
This commit is contained in:
parent
e33b503159
commit
ac28a37cd6
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
hostname-validator = "1.1.1"
|
||||
openssh = "0.10.4"
|
||||
ping = "0.5.2"
|
||||
regex = "1.10.5"
|
||||
thiserror = "1.0.61"
|
||||
tokio = { version = "1.38.0", features = ["rt-multi-thread"] }
|
||||
|
|
|
@ -1,35 +1,26 @@
|
|||
use ::thiserror::Error;
|
||||
use regex::Regex;
|
||||
|
||||
const VALID_IP_ADDRESS_PATTERN: &str = r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$";
|
||||
const VALID_HOSTNAME_PATTERN: &str = r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$";
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
#[error("The hostname \"{0}\" is invalid")]
|
||||
pub struct InvalidHostnameError(String);
|
||||
|
||||
pub struct Hostname {
|
||||
hostname: String,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl Hostname {
|
||||
pub fn new(hostname: &str) -> Result<Hostname, InvalidHostnameError> {
|
||||
if Self::is_valid_ip(&hostname) || Self::is_valid_hostname(&hostname) {
|
||||
if Self::is_valid_hostname(&hostname) {
|
||||
Ok(Hostname {
|
||||
hostname: hostname.to_string(),
|
||||
value: hostname.to_string(),
|
||||
})
|
||||
} else {
|
||||
Err(InvalidHostnameError(hostname.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_ip(ip: &str) -> bool {
|
||||
let re = Regex::new(VALID_IP_ADDRESS_PATTERN).unwrap();
|
||||
re.is_match(ip)
|
||||
}
|
||||
fn is_valid_hostname(hostname: &str) -> bool {
|
||||
let re = Regex::new(VALID_HOSTNAME_PATTERN).unwrap();
|
||||
re.is_match(hostname)
|
||||
hostname_validator::is_valid(hostname)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,12 +48,24 @@ mod tests {
|
|||
assert!(Hostname::new("is-42-the-response").is_ok());
|
||||
assert!(Hostname::new("GoB.NaSa-42.eu").is_ok());
|
||||
assert!(Hostname::new("256.127.63.31").is_ok());
|
||||
assert!(Hostname::new(
|
||||
"es.this-large-hostname-is-destined-to-work.so-you-dont-need-to-worry-for-anything.com"
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_hostnames() {
|
||||
assert!(Hostname::new("no..double.dots").is_err());
|
||||
assert!(Hostname::new("#illegal-character").is_err()); // We don't support IPv6 yet
|
||||
assert!(Hostname::new("#illegal-character").is_err());
|
||||
assert!(Hostname::new(
|
||||
"es.this-large-hostname-is-destined-to-panic-so-hard-that-it-will-break-computer.com"
|
||||
)
|
||||
.is_err());
|
||||
assert!(Hostname::new(
|
||||
"too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname.too-large-hostname"
|
||||
)
|
||||
.is_err());
|
||||
assert!(Hostname::new("").is_err());
|
||||
assert!(Hostname::new("not-end-with-hyphen-").is_err());
|
||||
}
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
use crate::hostname::Hostname;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Machine {
|
||||
name: String,
|
||||
hostname: Hostname,
|
||||
}
|
||||
|
||||
pub enum Status {
|
||||
Unreachable,
|
||||
Forbidden,
|
||||
Success(HashMap<String, String>),
|
||||
pub name: String,
|
||||
pub hostname: Hostname,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
|
@ -20,16 +13,3 @@ impl Machine {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Monitor {
|
||||
fn monitor(machine: &Machine) -> Status;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SshMonitor {}
|
||||
|
||||
impl Monitor for SshMonitor {
|
||||
fn monitor(machine: &Machine) -> Status {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -1,8 +1,26 @@
|
|||
use hostname::Hostname;
|
||||
use machine::Machine;
|
||||
use monitor::Monitor;
|
||||
use ssh_monitor::SshMonitor;
|
||||
|
||||
mod hostname;
|
||||
mod machine;
|
||||
mod ping_monitor;
|
||||
use machine::Machine;
|
||||
mod monitor;
|
||||
mod ssh_monitor;
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("Hello, world!");
|
||||
|
||||
let machine = Machine::new("kogasa", Hostname::new("kogasa").unwrap());
|
||||
|
||||
let status = SshMonitor::monitor(&machine).await;
|
||||
|
||||
println!("{}:", machine.name);
|
||||
|
||||
if let Ok(commands) = status {
|
||||
for (command, output) in commands.iter() {
|
||||
println!("{}: {}", command, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
use std::{collections::HashMap, error::Error};
|
||||
|
||||
use crate::machine::Machine;
|
||||
|
||||
pub trait Monitor {
|
||||
async fn monitor(machine: &Machine) -> Result<HashMap<String, String>, Box<dyn Error>>;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
use crate::machine::{Machine, Monitor, Status};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PingMonitor {}
|
||||
|
||||
impl Monitor for PingMonitor {
|
||||
fn monitor(machine: &Machine) -> Status {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn monitor_works() {
|
||||
assert!(true)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
use std::{collections::HashMap, error::Error};
|
||||
|
||||
use openssh::{KnownHosts, Session};
|
||||
|
||||
use crate::{machine::Machine, monitor::Monitor};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SshMonitor {}
|
||||
|
||||
impl Monitor for SshMonitor {
|
||||
async fn monitor(machine: &Machine) -> Result<HashMap<String, String>, Box<dyn Error>> {
|
||||
let result = Self::monitor_with_ssh(&machine).await;
|
||||
match result {
|
||||
Ok(commands) => Ok(commands),
|
||||
Err(ssh_error) => Err(Box::new(ssh_error)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SshMonitor {
|
||||
async fn monitor_with_ssh(
|
||||
machine: &Machine,
|
||||
) -> Result<HashMap<String, String>, openssh::Error> {
|
||||
let uri = format!("ssh://{}", &machine.hostname.value);
|
||||
let session = Session::connect(uri, KnownHosts::Accept).await?;
|
||||
let output = session.command("neofetch").output().await?;
|
||||
let mut commands = HashMap::new();
|
||||
commands.insert(
|
||||
"neofetch".to_string(),
|
||||
String::from_utf8(output.stdout).unwrap(),
|
||||
);
|
||||
|
||||
Ok(commands)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue