From 58b1daaec1cb144a3f2442be3bd684eb06619797 Mon Sep 17 00:00:00 2001 From: theMackabu <theMackabu@gmail.com> Date: Sun, 22 Oct 2023 11:01:36 -0700 Subject: [PATCH] message output from server now with colors --- Maidfile.toml | 6 +- crates/maid/client/src/server/cli.rs | 4 +- crates/maid/server/src/docker/run.rs | 205 ++++++++++++++------------- scripts/build.toml | 8 +- 4 files changed, 114 insertions(+), 109 deletions(-) diff --git a/Maidfile.toml b/Maidfile.toml index ee6b143..db36e24 100644 --- a/Maidfile.toml +++ b/Maidfile.toml @@ -22,7 +22,7 @@ VERSION='1.1.0' info = "Build binaries" depends = ["clean"] script = [ - "cargo zigbuild --release", + "cargo zigbuild --release --color always", "mv target/release/maid bin/maid", "mv target/release/maid_server bin/maid_server", "mv target/release/exit_test bin/exit_test" @@ -37,10 +37,10 @@ target = [ ] [tasks.build.remote] -silent = true +silent = false exclusive = false shell = "/bin/bash" -image = "rust:1.73" +image = "messense/cargo-zigbuild:latest" push = ["crates", "Cargo.toml", "Cargo.lock"] pull = "bin" diff --git a/crates/maid/client/src/server/cli.rs b/crates/maid/client/src/server/cli.rs index ef637b0..43eb731 100644 --- a/crates/maid/client/src/server/cli.rs +++ b/crates/maid/client/src/server/cli.rs @@ -128,7 +128,9 @@ pub fn remote(task: Task) { Ok(Message::Text(text)) => { if let Ok(Websocket { time: _, data, level }) = serde_json::from_str::<Websocket>(&text) { data.get("message").and_then(|m| m.as_str()).map(|msg| { - if !msg.is_empty() { + if level.as_str() == "none" { + print!("{msg}"); + } else { crate::log!(level.as_str(), "{}", msg); } }); diff --git a/crates/maid/server/src/docker/run.rs b/crates/maid/server/src/docker/run.rs index d6c3fd7..ddad9ca 100644 --- a/crates/maid/server/src/docker/run.rs +++ b/crates/maid/server/src/docker/run.rs @@ -1,24 +1,24 @@ -use crate::table; use crate::structs::ConnectionData; +use crate::table; -use bytes::Bytes; -use bollard::{Docker, errors::Error}; -use macros_rs::{string, fmtstr, str}; -use warp::ws::{Message, WebSocket}; +use bollard::container::{Config, DownloadFromContainerOptions, RemoveContainerOptions, UploadToContainerOptions}; +use bollard::exec::{CreateExecOptions, StartExecResults}; use bollard::image::CreateImageOptions; -use futures_util::{SinkExt, StreamExt}; +use bollard::{errors::Error, Docker}; +use bytes::Bytes; +use flate2::{write::GzEncoder, Compression}; use futures_core::Stream; use futures_util::stream::{SplitSink, SplitStream, TryStreamExt}; -use bollard::exec::{CreateExecOptions, StartExecResults}; -use bollard::container::{Config, RemoveContainerOptions, UploadToContainerOptions, DownloadFromContainerOptions}; +use futures_util::{SinkExt, StreamExt}; +use macros_rs::{fmtstr, str, string}; use std::default::Default; -use warp::hyper::Body; -use std::sync::Arc; -use tokio::sync::Mutex; +use std::io::Write; use std::path::PathBuf; +use std::sync::Arc; use text_placeholder::Template; -use flate2::{Compression, write::GzEncoder}; -use std::io::Write; +use tokio::sync::Mutex; +use warp::hyper::Body; +use warp::ws::{Message, WebSocket}; pub async fn concat_byte_stream<S>(s: S) -> Result<Vec<u8>, Error> where @@ -32,13 +32,13 @@ where } // add error handling to all the unwraps -pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSocket>, docker: Docker) -> Result<(), Box<dyn std::error::Error + 'static>> { +pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSocket>, docker: Docker) -> Result<(), Box<dyn std::error::Error + 'static>> { let mut parsed: Option<ConnectionData> = None; let tx_ref = Arc::new(Mutex::new(tx)); - + while parsed.is_none() { if let Some(result) = rx.next().await { - let msg = result.unwrap(); + let msg = result.unwrap(); match serde_json::from_str::<ConnectionData>(msg.to_str().unwrap()) { Ok(value) => { parsed = Some(value); @@ -49,10 +49,10 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock } } } - + let parsed = parsed.unwrap(); let name = &parsed.info.name; - + println!("creating container for task [{name}]"); docker .create_image( @@ -65,25 +65,22 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock ) .for_each(|msg| { let tx_ref = Arc::clone(&tx_ref); - + async move { let msg = msg.as_ref().expect("Failed to get CreateImageInfo"); - let formatted = format!( - "{} {}", - msg.status.clone().unwrap_or_else(|| string!("Waiting")), - msg.progress.clone().unwrap_or_else(|| string!("")) - ); - + let formatted = format!("{} {}", msg.status.clone().unwrap_or_else(|| string!("Waiting")), msg.progress.clone().unwrap_or_else(|| string!(""))); + let mut tx_lock = tx_ref.lock().await; - tx_lock.send(Message::text( - serde_json::to_string(&serde_json::json!({ - "level": "docker", - "time": chrono::Utc::now().timestamp_millis(), - "data": { "message": formatted }, - })) - .unwrap(), - )) - .await; + tx_lock + .send(Message::text( + serde_json::to_string(&serde_json::json!({ + "level": "docker", + "time": chrono::Utc::now().timestamp_millis(), + "data": { "message": formatted }, + })) + .unwrap(), + )) + .await; } }) .await; @@ -96,60 +93,58 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock let id = docker.create_container::<&str, String>(None, config).await?.id; println!("created container"); - + docker.start_container::<String>(&id, None).await?; println!("started container"); - + let tx_ref = Arc::clone(&tx_ref); let mut tx_lock = tx_ref.lock().await; - - tx_lock.send(Message::text( - serde_json::to_string(&serde_json::json!({ - "level": "success", - "time": chrono::Utc::now().timestamp_millis(), - "data": { "binary": true }, - })) - .unwrap(), - )) - .await - .unwrap(); - + + tx_lock + .send(Message::text( + serde_json::to_string(&serde_json::json!({ + "level": "success", + "time": chrono::Utc::now().timestamp_millis(), + "data": { "binary": true }, + })) + .unwrap(), + )) + .await + .unwrap(); + if let Some(result) = rx.next().await { println!("received message: binary"); - + let msg = result.unwrap(); - fn bytes_to_body(bytes: &[u8]) -> Body { - Body::from(bytes.to_vec()) - } - + fn bytes_to_body(bytes: &[u8]) -> Body { Body::from(bytes.to_vec()) } + // note: this `Result` may be an `Err` variant, which should be handled // help: use `let _ = ...` to ignore the resulting value - docker.upload_to_container(&id, Some(UploadToContainerOptions{ path: "/opt", ..Default::default() }), bytes_to_body(&msg.as_bytes())).await; + docker + .upload_to_container(&id, Some(UploadToContainerOptions { path: "/opt", ..Default::default() }), bytes_to_body(&msg.as_bytes())) + .await; println!("wrote tarfile to container"); } - + let dependencies = match &parsed.maidfile.tasks[&parsed.info.name].depends { - Some(deps) => { + Some(deps) => { let mut dep_script: Vec<String> = vec![]; for item in deps.iter() { dep_script.push( parsed.maidfile.tasks[item] .script .as_array() - .map(|arr| { - arr.iter() - .map(|val| val.as_str().unwrap_or_default()) - .collect::<Vec<_>>() - .join("\n") - }) - .unwrap_or_default() + .map(|arr| arr.iter().map(|val| val.as_str().unwrap_or_default()).collect::<Vec<_>>().join("\n")) + .unwrap_or_default(), ); - }; + } dep_script.join("\n") } - None => { string!("") } + None => { + string!("") + } }; - + // move common things such as structs and helpers to seperate crate let table = table::create(parsed.maidfile.clone(), &parsed.info.args, PathBuf::new().join("/opt")); let script = Template::new_with_placeholder(str!(parsed.info.script.join("\n")), "%{", "}").fill_with_hashmap(&table); @@ -161,7 +156,11 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock CreateExecOptions { attach_stdout: Some(true), attach_stderr: Some(true), - cmd: Some(vec![str!(parsed.info.remote.shell), "-c", fmtstr!("cd /opt && touch script.sh && echo '{dependencies}\n{script}' > script.sh && chmod +x script.sh && ./script.sh")]), + cmd: Some(vec![ + str!(parsed.info.remote.shell), + "-c", + fmtstr!("cd /opt && touch script.sh && echo '{dependencies}\n{script}' > script.sh && chmod +x script.sh && ./script.sh"), + ]), ..Default::default() }, ) @@ -169,59 +168,63 @@ pub async fn exec(tx: SplitSink<WebSocket, Message>, mut rx: SplitStream<WebSock .id; if let StartExecResults::Attached { mut output, .. } = docker.start_exec(&exec, None).await? { - tx_lock.send(Message::text( - serde_json::to_string(&serde_json::json!({ - "level": "build", - "time": chrono::Utc::now().timestamp_millis(), - "data": { "message": "waiting for build to finish..." }, - })) - .unwrap(), - )) - .await - .unwrap(); - + tx_lock + .send(Message::text( + serde_json::to_string(&serde_json::json!({ + "level": "build", + "time": chrono::Utc::now().timestamp_millis(), + "data": { "message": "waiting for build to finish..." }, + })) + .unwrap(), + )) + .await + .unwrap(); + while let Some(Ok(msg)) = output.next().await { if !parsed.info.remote.silent { - let parsed = format!("{msg}"); - if parsed != "" { - tx_lock.send(Message::text( + tx_lock + .send(Message::text( serde_json::to_string(&serde_json::json!({ - "level": "build", + "level": "none", "time": chrono::Utc::now().timestamp_millis(), - "data": { "message": parsed.trim() }, + "data": { "message": msg.to_string() }, })) .unwrap(), )) .await .unwrap(); - } } } } - - let res = docker.download_from_container(&id, Some(DownloadFromContainerOptions{ path: fmtstr!("/opt/{}", parsed.info.remote.pull.clone()) })); + + let res = docker.download_from_container( + &id, + Some(DownloadFromContainerOptions { + path: fmtstr!("/opt/{}", parsed.info.remote.pull.clone()), + }), + ); let bytes = concat_byte_stream(res).await?; let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); - + encoder.write_all(&bytes)?; let compressed_data = encoder.finish()?; - + tx_lock.send(Message::binary(compressed_data)).await.unwrap(); println!("sent message: binary, from [{}]", parsed.info.remote.pull); - - tx_lock.send(Message::text( - serde_json::to_string(&serde_json::json!({ - "level": "success", - "time": chrono::Utc::now().timestamp_millis(), - "data": { "done": true, "message": "" }, - })) - .unwrap(), - )) - .await - .unwrap(); + + tx_lock + .send(Message::text( + serde_json::to_string(&serde_json::json!({ + "level": "success", + "time": chrono::Utc::now().timestamp_millis(), + "data": { "done": true }, + })) + .unwrap(), + )) + .await + .unwrap(); println!("sent message: [done]"); - - + println!("deleted old container"); // delete container if socket closed docker.remove_container(&id, Some(RemoveContainerOptions { force: true, ..Default::default() })).await?; diff --git a/scripts/build.toml b/scripts/build.toml index 1ee5bd0..f1e7544 100644 --- a/scripts/build.toml +++ b/scripts/build.toml @@ -8,25 +8,25 @@ script = [ "mkdir build", # build linux (x86_64) - "cargo zigbuild -r -p maid", + "cargo zigbuild -r -p maid --color always", "mv target/release/maid build/maid", "zip build/maid_%{env.VERSION}_linux_amd64.zip build/maid", "rm build/maid", # build windows (x86_64) - "cargo zigbuild -r -p maid --target x86_64-pc-windows-gnu", + "cargo zigbuild -r -p maid --target x86_64-pc-windows-gnu --color always", "mv target/x86_64-pc-windows-gnu/release/maid.exe build/maid.exe", "zip build/maid_%{env.VERSION}_windows_amd64.zip build/maid.exe", "rm build/maid.exe", # build macos (x86_64) - "cargo zigbuild -r -p maid --target x86_64-apple-darwin", + "cargo zigbuild -r -p maid --target x86_64-apple-darwin --color always", "mv target/x86_64-apple-darwin/release/maid build/maid", "zip build/maid_%{env.VERSION}_darwin_amd64.zip build/maid", "rm build/maid", # build macos (aarch64) - "cargo zigbuild -r -p maid --target aarch64-apple-darwin", + "cargo zigbuild -r -p maid --target aarch64-apple-darwin --color always", "mv target/aarch64-apple-darwin/release/maid build/maid", "zip build/maid_%{env.VERSION}_darwin_arm.zip build/maid", "rm build/maid", -- GitLab