diff --git a/Cargo.toml b/Cargo.toml index c0fff6b..07562e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,13 +49,17 @@ sha2 = "0.10.7" # SHA-2 hash functions tempfile = "3.5" # For temporary file operations tera = "1.19.0" # Template engine for text rendering thiserror = "2.0.12" # For error handling -tokio = "1.45.0" +tokio = { version = "1.45.0", features = ["full"] } tokio-postgres = "0.7.8" # Async PostgreSQL client tokio-test = "0.4.4" uuid = { version = "1.16.0", features = ["v4"] } reqwest = { version = "0.12.15", features = ["json"] } urlencoding = "2.1.3" zinit-client = "0.3.0" +russh = "0.42.0" +russh-keys = "0.42.0" +async-trait = "0.1.81" +futures = "0.3.30" # Optional features for specific OS functionality [target.'cfg(unix)'.dependencies] diff --git a/examples/containers/buildah_run.rhai b/examples/containers/buildah_run.rhai index 3bb4d4b..18c5ac1 100644 --- a/examples/containers/buildah_run.rhai +++ b/examples/containers/buildah_run.rhai @@ -9,7 +9,7 @@ println(`Using local image: ${local_image_name}`); // Import the image from buildah to nerdctl println("Importing image from buildah to nerdctl..."); -process_run("buildah", ["push", "custom-golang-nginx:latest", "docker-daemon:custom-golang-nginx:latest"]); +process_run("buildah", ["push", "custom-golang-nginx:latest", "docker-daemon:localhost/custom-golang-nginx:latest"]); let tag_result = nerdctl_image_tag("custom-golang-nginx:latest", local_image_name); diff --git a/examples/process/run_log.rhai b/examples/process/run_log.rhai index fe36e6e..507c33f 100644 --- a/examples/process/run_log.rhai +++ b/examples/process/run_log.rhai @@ -2,7 +2,7 @@ print("Running a command using run().log().do()..."); // The .log() method will print the command string to the console before execution. // This is useful for debugging or tracing which commands are being run. -let result = run("echo This command is logged").log().do(); +let result = run("echo This command is logged").log().execute(); print(`Command finished.`); print(`Success: ${result.success}`); diff --git a/installers/nerdctl.rhai b/installers/nerdctl.rhai index ca15967..c8de4e1 100644 --- a/installers/nerdctl.rhai +++ b/installers/nerdctl.rhai @@ -8,14 +8,23 @@ fn nerdctl_download(){ copy_bin(`/tmp/${name}/*`); delete(`/tmp/${name}`); + screen_kill("containerd"); let name="containerd"; let url="https://github.com/containerd/containerd/releases/download/v2.1.2/containerd-2.1.2-linux-amd64.tar.gz"; download(url,`/tmp/${name}`,20000); - copy_bin(`/tmp/${name}/bin/*`); + // copy_bin(`/tmp/${name}/bin/*`); delete(`/tmp/${name}`); - screen_kill("containerd"); + let cfg = ` + [[registry]] + location = "localhost:5000" + insecure = true + `; + file_write("/etc/containers/registries.conf", dedent(cfg)); screen_new("containerd", "containerd"); + sleep(1); + nerdctl_remove_all(); + run("nerdctl run -d -p 5000:5000 --name registry registry:2").log().execute(); package_install("buildah"); package_install("runc"); diff --git a/src/lib.rs b/src/lib.rs index 91162b7..d19c078 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ pub mod virt; pub mod vault; pub mod zinit_client; pub mod mycelium; +pub mod net; // Version information /// Returns the version of the SAL library diff --git a/src/net/http.rs b/src/net/http.rs new file mode 100644 index 0000000..746b6fd --- /dev/null +++ b/src/net/http.rs @@ -0,0 +1,51 @@ +use reqwest::Client; +use std::time::Duration; + +// HTTP Checker +pub struct HttpChecker { + client: Client, + url: String, +} + +impl HttpChecker { + pub async fn check_url(&self) -> Result { + let res = self.client.get(&self.url).send().await?; + Ok(res.status().is_success()) + } +} + +// HTTP Checker Builder +pub struct HttpCheckerBuilder { + url: String, + timeout: Duration, +} + +impl HttpCheckerBuilder { + pub fn new() -> Self { + Self { + url: "http://localhost".to_string(), + timeout: Duration::from_secs(30), + } + } + + pub fn url>(mut self, url: S) -> Self { + self.url = url.into(); + self + } + + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + pub fn build(self) -> HttpChecker { + let client = Client::builder() + .timeout(self.timeout) + .build() + .expect("Failed to build HTTP client"); + HttpChecker { + client, + url: self.url, + } + } +} \ No newline at end of file diff --git a/src/net/mod.rs b/src/net/mod.rs new file mode 100644 index 0000000..6ed6308 --- /dev/null +++ b/src/net/mod.rs @@ -0,0 +1,3 @@ +pub mod ssh; +pub mod tcp; +pub mod http; \ No newline at end of file diff --git a/src/net/ssh.rs b/src/net/ssh.rs new file mode 100644 index 0000000..8dfc738 --- /dev/null +++ b/src/net/ssh.rs @@ -0,0 +1,133 @@ +use russh::client; +use russh_keys::key; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::Duration; + +// SSH Connection +#[derive(Clone)] +pub struct SshConnection { + session: Arc>, +} + +impl SshConnection { + pub async fn ping(&self) -> Result<(), anyhow::Error> { + let mut channel = self.session.channel_open_session().await?; + channel.exec(true, "ping -c 1 127.0.0.1").await?; + Ok(()) + } +} + +// SSH Connection Builder +pub struct SshConnectionBuilder { + host: String, + port: u16, + user: String, + password: Option, + key_path: Option, + use_agent: bool, + timeout: Duration, +} + +impl SshConnectionBuilder { + pub fn new() -> Self { + Self { + host: "localhost".to_string(), + port: 22, + user: "root".to_string(), + password: None, + key_path: None, + use_agent: true, + timeout: Duration::from_secs(30), + } + } + + pub fn host>(mut self, host: S) -> Self { + self.host = host.into(); + self + } + + pub fn port(mut self, port: u16) -> Self { + self.port = port; + self + } + + pub fn user>(mut self, user: S) -> Self { + self.user = user.into(); + self + } + + pub fn password>(mut self, password: S) -> Self { + self.password = Some(password.into()); + self + } + + pub fn key_path(mut self, key_path: PathBuf) -> Self { + self.key_path = Some(key_path); + self + } + + pub fn use_agent(mut self, use_agent: bool) -> Self { + self.use_agent = use_agent; + self + } + + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + pub async fn build(self) -> Result { + let config = Arc::new(client::Config::default()); + let sh = Client; + + let mut session = client::connect(config, (self.host.as_str(), self.port), sh).await?; + + let auth_res = if self.use_agent { + let mut agent = russh_keys::agent::client::AgentClient::connect_env().await?; + let mut keys = agent.request_identities().await?; + if keys.is_empty() { + return Err(anyhow::anyhow!("No identities found in ssh-agent")); + } + let key = keys.remove(0); + let (_agent, authed) = session + .authenticate_future(self.user.as_str(), Arc::new(key), agent) + .await; + authed? + } else if let Some(password) = self.password { + session + .authenticate_password(self.user.as_str(), &password) + .await? + } else if let Some(key_path) = self.key_path { + let key_pair = russh_keys::load_secret_key(key_path, None)?; + session + .authenticate_publickey(self.user.as_str(), Arc::new(key_pair)) + .await? + } else { + return Err(anyhow::anyhow!( + "No authentication method specified" + )); + }; + + if !auth_res { + return Err(anyhow::anyhow!("Authentication failed")); + } + + Ok(SshConnection { + session: Arc::new(session), + }) + } +} + +struct Client; + +impl client::Handler for Client { + type Error = russh::Error; + + fn check_server_key( + &mut self, + _server_public_key: &key::PublicKey, + ) -> std::future::Ready> { + std::future::ready(Ok(true)) + } +} \ No newline at end of file diff --git a/src/net/tcp.rs b/src/net/tcp.rs new file mode 100644 index 0000000..0082654 --- /dev/null +++ b/src/net/tcp.rs @@ -0,0 +1,64 @@ +use std::net::{SocketAddr, TcpStream}; +use std::time::Duration; + +// TCP Checker +pub struct TcpChecker { + host: String, + port: u16, + timeout: Duration, +} + +impl TcpChecker { + pub fn ping(&self) -> Result<(), std::io::Error> { + let addr = format!("{}:{}", self.host, self.port); + let socket_addr: SocketAddr = addr.parse().expect("Failed to parse socket address"); + TcpStream::connect_timeout(&socket_addr, self.timeout)?; + Ok(()) + } + + pub fn check_port(&self) -> bool { + let addr = format!("{}:{}", self.host, self.port); + let socket_addr: SocketAddr = addr.parse().expect("Failed to parse socket address"); + TcpStream::connect_timeout(&socket_addr, self.timeout).is_ok() + } +} + +// TCP Checker Builder +pub struct TcpCheckerBuilder { + host: String, + port: u16, + timeout: Duration, +} + +impl TcpCheckerBuilder { + pub fn new() -> Self { + Self { + host: "localhost".to_string(), + port: 80, + timeout: Duration::from_secs(1), + } + } + + pub fn host>(mut self, host: S) -> Self { + self.host = host.into(); + self + } + + pub fn port(mut self, port: u16) -> Self { + self.port = port; + self + } + + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + pub fn build(self) -> TcpChecker { + TcpChecker { + host: self.host, + port: self.port, + timeout: self.timeout, + } + } +} \ No newline at end of file