diff --git a/nomact/build.rs b/nomact/build.rs new file mode 100644 index 0000000..4d774f5 --- /dev/null +++ b/nomact/build.rs @@ -0,0 +1,17 @@ +fn main() -> Result<(), Box> { + tonic_prost_build::configure() + .build_server(false) + .compile_protos( + &["../actions-proto-def/proto/runner/v1/services.proto"], + &["../actions-proto-def/proto"], + )?; + + tonic_prost_build::configure() + .build_server(false) + .compile_protos( + &["../actions-proto-def/proto/ping/v1/services.proto"], + &["../actions-proto-def/proto"], + )?; + + Ok(()) +} diff --git a/nomact/src/lib.rs b/nomact/src/lib.rs new file mode 100644 index 0000000..344f9f5 --- /dev/null +++ b/nomact/src/lib.rs @@ -0,0 +1,115 @@ +use std::vec; + +pub mod runner { + tonic::include_proto!("runner.v1"); +} + +pub mod ping { + tonic::include_proto!("ping.v1"); +} + +pub struct RegisterConfig { + pub name: String, + pub register_token: String, + pub instance: hyper::Uri, + pub version: String, + pub labels: Vec, + pub ephemeral: bool, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct RegisteredRunner { + pub uuid: String, + pub token: String, +} + +pub fn instance_to_api_url(instance: hyper::Uri) -> hyper::Uri { + let mut parts = instance.into_parts(); + parts.path_and_query = Some("/api/actions".try_into().unwrap()); + hyper::Uri::from_parts(parts).unwrap() +} + +pub async fn register(conf: RegisterConfig) -> Result { + use hyper_rustls::ConfigBuilderExt; + + let tls = rustls::ClientConfig::builder() + .with_native_roots() + .unwrap() + .with_no_client_auth(); + // Prepare the HTTPS connector + let https = hyper_rustls::HttpsConnectorBuilder::new() + .with_tls_config(tls) + .https_or_http() + .enable_http1() + .build(); + + // Must use hyper directly... + let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build(https); + + let svc = tower::ServiceBuilder::new() + .layer(tonic_web::GrpcWebClientLayer::new()) + .service(client); + + let uri = instance_to_api_url(conf.instance); + + let mut client = runner::runner_service_client::RunnerServiceClient::with_origin(svc, uri); + + let request = tonic::Request::new(runner::RegisterRequest { + name: conf.name, + token: conf.register_token, + version: conf.version, + labels: conf.labels, + ephemeral: conf.ephemeral, + ..Default::default() + }); + + let response = client.register(request).await.unwrap(); + + match response.into_inner().runner { + Some(runner) => Ok(RegisteredRunner { + uuid: runner.uuid, + token: runner.token, + }), + None => Err(()), + } +} + +pub async fn run_task( + client: &mut runner::runner_service_client::RunnerServiceClient, + task: runner::Task, +) where + T: tonic::client::GrpcService, + T::Error: Into>, + T::ResponseBody: http_body::Body + std::marker::Send + 'static, + ::Error: + Into> + std::marker::Send, +{ + tracing::debug!(?task, "Run-Task"); + + let workflow = core::str::from_utf8(task.workflow_payload()).unwrap(); + tracing::info!("Raw-Workflow: {}", workflow); + + let task_id = task.id; + let task_workflow = core::str::from_utf8(task.workflow_payload()).unwrap(); + let task_context = task.context.as_ref(); + let task_secrets = &task.secrets; + let task_needs = &task.needs; + let task_vars = &task.vars; + + let workflow = serde_yaml::from_slice::(task.workflow_payload()); + tracing::info!("Workflow: {:?}", &workflow); + + let update_req = tonic::Request::new(runner::UpdateTaskRequest { + outputs: std::collections::HashMap::new(), + state: Some(runner::TaskState { + id: task.id, + result: runner::Result::Failure.into(), + started_at: None, + stopped_at: None, + steps: vec![], + }), + }); + let resp = client.update_task(update_req).await.unwrap(); + tracing::info!(resp = tracing::field::debug(resp.get_ref()), "Update Task State"); +} diff --git a/nomact/src/main.rs b/nomact/src/main.rs new file mode 100644 index 0000000..fe130cd --- /dev/null +++ b/nomact/src/main.rs @@ -0,0 +1,122 @@ +use hyper_rustls::ConfigBuilderExt; +use tracing_subscriber::layer::SubscriberExt; + +use nomact::runner; + +fn main() { + let registry = tracing_subscriber::registry().with(tracing_subscriber::fmt::layer()); + tracing::subscriber::set_global_default(registry).unwrap(); + + tracing::info!("Starting..."); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + let register_token = include_str!("../../register_token"); + let instance_url = include_str!("../../instance_url"); + let name = "Testing"; + + let runner = rt.block_on(async move { + let path = "./runner_state.json"; + match tokio::fs::try_exists(path).await { + Ok(true) => { + tracing::info!("Using already registered runner"); + + let content = tokio::fs::read(path).await.unwrap(); + serde_json::from_slice::(&content).unwrap() + } + _ => { + tracing::info!("Registering new runner"); + + let runner = nomact::register(nomact::RegisterConfig { + name: name.into(), + register_token: register_token.into(), + instance: hyper::Uri::from_static(instance_url), + version: "0.0.1".into(), + labels: Vec::new(), + ephemeral: false, + }) + .await + .unwrap(); + tracing::info!(?runner, "Registered Runner"); + + let serialized = serde_json::to_vec(&runner).unwrap(); + tokio::fs::write(path, serialized).await.unwrap(); + + runner + } + } + }); + + rt.block_on(async move { + let tls = rustls::ClientConfig::builder() + .with_native_roots() + .unwrap() + .with_no_client_auth(); + // Prepare the HTTPS connector + let https = hyper_rustls::HttpsConnectorBuilder::new() + .with_tls_config(tls) + .https_or_http() + .enable_http1() + .build(); + + // Must use hyper directly... + let client = + hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build(https); + + let svc = tower::ServiceBuilder::new() + .layer(tonic_web::GrpcWebClientLayer::new()) + .service(client); + + let mut client = runner::runner_service_client::RunnerServiceClient::with_origin( + tonic::service::interceptor::InterceptedService::new( + svc, + move |mut req: tonic::Request<()>| { + req.metadata_mut() + .insert("x-runner-uuid", runner.uuid.as_str().try_into().unwrap()); + req.metadata_mut() + .insert("x-runner-token", runner.token.as_str().try_into().unwrap()); + + Ok(req) + }, + ), + nomact::instance_to_api_url(hyper::Uri::from_static(instance_url)) + ); + + let request = tonic::Request::new(runner::DeclareRequest { + version: "0.0.2".into(), + labels: vec![ + "ubuntu-latest".into(), + "ubuntu-22.04".into(), + "ubuntu-20.04".into(), + ], + }); + + let response = client.declare(request).await.unwrap(); + tracing::info!(runner = tracing::field::debug(response.get_ref().runner.as_ref()), "Declare current runner state"); + + + + let mut task_id = 0; + loop { + tracing::info!("Checking for new Task"); + + let request = tonic::Request::new(runner::FetchTaskRequest { + tasks_version: task_id, + }); + + let response = client.fetch_task(request).await.unwrap(); + + task_id = response.get_ref().tasks_version; + + if let Some(task) = response.into_inner().task { + nomact::run_task(&mut client, task).await; + } + + tokio::time::sleep(std::time::Duration::from_secs(30)).await; + } + }); +}