Compare commits
8 Commits
8afea6fc81
...
8b0e05ba14
Author | SHA1 | Date |
---|---|---|
Aravinth Manivannan | 8b0e05ba14 | |
Aravinth Manivannan | d6a5853d34 | |
Aravinth Manivannan | a63979af76 | |
Aravinth Manivannan | b3a3fe8c0d | |
Aravinth Manivannan | 501f44560c | |
Aravinth Manivannan | adaebe4e35 | |
Aravinth Manivannan | 3f4b8d363e | |
Aravinth Manivannan | db161fe6f0 |
12
Cargo.toml
12
Cargo.toml
|
@ -1,9 +1,16 @@
|
|||
[package]
|
||||
name = "ftest"
|
||||
version = "0.1.0"
|
||||
description = "Compliance checker for the software forge federation ecosystem"
|
||||
homepage = "https://docs.forgeflux.org/ftest/about"
|
||||
repository = "https://git.batsense.net/ForgeFlux/ftest"
|
||||
documentation = "https://docs.forgeflux.org/ftest/about"
|
||||
license = "AGPLv3 or later version"
|
||||
authors = ["realaravinth <realaravinth@batsense.net>"]
|
||||
edition = "2021"
|
||||
default-run = "ftest"
|
||||
build = "build.rs"
|
||||
defalt-run ="./src/main.rs"
|
||||
|
||||
|
||||
[[bin]]
|
||||
name ="config_validator"
|
||||
|
@ -40,12 +47,13 @@ semver = { version = "1.0.18", features = ["serde"] }
|
|||
toml = "0.7.6"
|
||||
tokio = { version = "1.32.0", features = ["sync", "time"] }
|
||||
clap = { version = "4.4.6", features = ["derive"] }
|
||||
actix-rt = "2.7.0"
|
||||
|
||||
[build-dependencies]
|
||||
serde_json = "1"
|
||||
sqlx = { version = "0.6.1", features = [ "runtime-actix-rustls", "postgres", "time", "offline"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2.7.0"
|
||||
#actix-rt = "2.7.0"
|
||||
base64 = "0.13.0"
|
||||
mktemp = "0.5.1"
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
use actix_web::{web, HttpResponse, Responder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{GIT_COMMIT_HASH, VERSION};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct BuildDetails {
|
||||
pub version: &'static str,
|
||||
pub git_commit_hash: &'static str,
|
||||
}
|
||||
|
||||
pub mod routes {
|
||||
pub struct Meta {
|
||||
pub build_details: &'static str,
|
||||
pub health: &'static str,
|
||||
}
|
||||
|
||||
impl Meta {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
build_details: "/api/v1/meta/build",
|
||||
health: "/api/v1/meta/health",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const META: routes::Meta = routes::Meta::new();
|
||||
|
||||
/// emmits build details of the bninary
|
||||
#[actix_web_codegen_const_routes::get(path = "META.build_details")]
|
||||
async fn build_details() -> impl Responder {
|
||||
let build = BuildDetails {
|
||||
version: VERSION,
|
||||
git_commit_hash: GIT_COMMIT_HASH,
|
||||
};
|
||||
HttpResponse::Ok().json(build)
|
||||
}
|
||||
|
||||
pub fn services(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(build_details);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_web::{http::StatusCode, test, App};
|
||||
|
||||
use super::services;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn build_details_works() {
|
||||
let app = test::init_service(App::new().configure(services)).await;
|
||||
|
||||
let resp = test::call_service(
|
||||
&app,
|
||||
test::TestRequest::get()
|
||||
.uri(super::META.build_details)
|
||||
.to_request(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
pub mod meta;
|
||||
pub mod webhooks;
|
||||
|
||||
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
||||
webhooks::services(cfg);
|
||||
meta::services(cfg);
|
||||
}
|
||||
|
|
193
src/ctx.rs
193
src/ctx.rs
|
@ -26,95 +26,6 @@ pub trait FullAppContext: std::marker::Send + std::marker::Sync + CloneFullAppCt
|
|||
fn port(&self) -> u32;
|
||||
}
|
||||
|
||||
pub trait MinAppContext: std::marker::Send + std::marker::Sync {
|
||||
fn docker_(&self) -> Arc<dyn DockerLike>;
|
||||
fn results_(&self) -> &ResultStore;
|
||||
fn port_(&self) -> u32;
|
||||
}
|
||||
|
||||
impl MinAppContext for Arc<dyn FullAppContext> {
|
||||
fn docker_(&self) -> Arc<dyn DockerLike> {
|
||||
self.docker()
|
||||
}
|
||||
fn results_(&self) -> &ResultStore {
|
||||
self.results()
|
||||
}
|
||||
fn port_(&self) -> u32 {
|
||||
self.port()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ctx {
|
||||
settings: Settings,
|
||||
db: Database,
|
||||
results: ResultStore,
|
||||
docker: Arc<dyn DockerLike>,
|
||||
}
|
||||
|
||||
impl FullAppContext for Ctx {
|
||||
fn settings(&self) -> &Settings {
|
||||
&self.settings
|
||||
}
|
||||
fn db(&self) -> &Database {
|
||||
&self.db
|
||||
}
|
||||
fn docker(&self) -> Arc<dyn DockerLike> {
|
||||
self.docker.clone()
|
||||
}
|
||||
fn results(&self) -> &ResultStore {
|
||||
&self.results
|
||||
}
|
||||
fn port(&self) -> u32 {
|
||||
self.settings.server.port
|
||||
}
|
||||
}
|
||||
|
||||
impl MinAppContext for Ctx {
|
||||
fn docker_(&self) -> Arc<dyn DockerLike> {
|
||||
self.docker()
|
||||
}
|
||||
fn results_(&self) -> &ResultStore {
|
||||
self.results()
|
||||
}
|
||||
fn port_(&self) -> u32 {
|
||||
self.port()
|
||||
}
|
||||
}
|
||||
|
||||
//impl MinAppContext for Arc<dyn MinAppContext> {
|
||||
// fn docker_(&self) -> Arc<dyn DockerLike>{
|
||||
// self.docker_()
|
||||
// }
|
||||
// fn results_(&self) -> &ResultStore {
|
||||
// self.results_()
|
||||
// }
|
||||
// fn port_(&self) -> u32 {
|
||||
// self.port_()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//impl FullAppContext for Arc<dyn FullAppContext> {
|
||||
// fn settings(&self) -> &Settings {
|
||||
// self.settings()
|
||||
// }
|
||||
// fn db(&self) -> &Database {
|
||||
// self.db()
|
||||
// }
|
||||
//
|
||||
// fn docker(&self) -> Arc<dyn DockerLike>{
|
||||
// self.docker()
|
||||
// }
|
||||
// fn results(&self) -> &ResultStore {
|
||||
// self.results()
|
||||
// }
|
||||
// fn port(&self) -> u32 {
|
||||
// self.port()
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
|
||||
pub trait CloneFullAppCtx {
|
||||
fn clone_f(&self) -> Box<dyn FullAppContext>;
|
||||
}
|
||||
|
@ -134,28 +45,65 @@ impl Clone for Box<dyn FullAppContext> {
|
|||
}
|
||||
}
|
||||
|
||||
//pub trait CloneMinAppContext {
|
||||
// fn clone_fi(&self) -> Box<dyn MinAppContext>;
|
||||
//}
|
||||
//
|
||||
//impl<T> CloneMinAppContext for T
|
||||
//where
|
||||
// T: CloneMinAppContext + Clone + 'static,
|
||||
//{
|
||||
// fn clone_fi(&self) -> Box<dyn MinAppContext> {
|
||||
// Box::new(self.clone())
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//impl Clone for Box<dyn MinAppContext> {
|
||||
// fn clone(&self) -> Self {
|
||||
// (**self).clone_fi()
|
||||
// }
|
||||
//}
|
||||
pub trait MinAppContext: std::marker::Send + std::marker::Sync {
|
||||
fn docker_(&self) -> Arc<dyn DockerLike>;
|
||||
fn results_(&self) -> &ResultStore;
|
||||
fn port_(&self) -> u32;
|
||||
}
|
||||
|
||||
impl MinAppContext for Arc<dyn FullAppContext> {
|
||||
fn docker_(&self) -> Arc<dyn DockerLike> {
|
||||
self.docker()
|
||||
}
|
||||
fn results_(&self) -> &ResultStore {
|
||||
self.results()
|
||||
}
|
||||
fn port_(&self) -> u32 {
|
||||
self.port()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DaemonCtx {
|
||||
settings: Settings,
|
||||
db: Database,
|
||||
results: ResultStore,
|
||||
docker: Arc<dyn DockerLike>,
|
||||
}
|
||||
|
||||
impl FullAppContext for DaemonCtx {
|
||||
fn settings(&self) -> &Settings {
|
||||
&self.settings
|
||||
}
|
||||
fn db(&self) -> &Database {
|
||||
&self.db
|
||||
}
|
||||
fn docker(&self) -> Arc<dyn DockerLike> {
|
||||
self.docker.clone()
|
||||
}
|
||||
fn results(&self) -> &ResultStore {
|
||||
&self.results
|
||||
}
|
||||
fn port(&self) -> u32 {
|
||||
self.settings.server.port
|
||||
}
|
||||
}
|
||||
|
||||
impl MinAppContext for DaemonCtx {
|
||||
fn docker_(&self) -> Arc<dyn DockerLike> {
|
||||
self.docker()
|
||||
}
|
||||
fn results_(&self) -> &ResultStore {
|
||||
self.results()
|
||||
}
|
||||
fn port_(&self) -> u32 {
|
||||
self.port()
|
||||
}
|
||||
}
|
||||
|
||||
pub type ResultStore = Arc<RwLock<HashMap<String, Sender<CResult>>>>;
|
||||
|
||||
impl Ctx {
|
||||
impl DaemonCtx {
|
||||
pub async fn new(settings: Settings) -> Self {
|
||||
let results = HashMap::default();
|
||||
let results = Arc::new(RwLock::new(results));
|
||||
|
@ -168,3 +116,32 @@ impl Ctx {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CliCtx {
|
||||
results: ResultStore,
|
||||
docker: Arc<dyn DockerLike>,
|
||||
}
|
||||
|
||||
impl MinAppContext for CliCtx {
|
||||
fn docker_(&self) -> Arc<dyn DockerLike> {
|
||||
self.docker.clone()
|
||||
}
|
||||
fn results_(&self) -> &ResultStore {
|
||||
&self.results
|
||||
}
|
||||
fn port_(&self) -> u32 {
|
||||
80
|
||||
}
|
||||
}
|
||||
|
||||
impl CliCtx {
|
||||
pub fn new() -> Self {
|
||||
let results = HashMap::default();
|
||||
let results = Arc::new(RwLock::new(results));
|
||||
Self {
|
||||
results,
|
||||
docker: Arc::new(Docker::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
104
src/main.rs
104
src/main.rs
|
@ -4,19 +4,22 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::{
|
||||
error::InternalError, http::StatusCode, middleware as actix_middleware, web::Data as WebData,
|
||||
web::JsonConfig, App, HttpServer,
|
||||
};
|
||||
//use clap::{Parser, Subcommand};
|
||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||
//use static_assets::FileMap;
|
||||
use tracing::info;
|
||||
use tracing_actix_web::TracingLogger;
|
||||
//
|
||||
//pub use crate::api::v1::ROUTES as V1_API_ROUTES;
|
||||
use ctx::Ctx;
|
||||
use ctx::CliCtx;
|
||||
use ctx::DaemonCtx;
|
||||
use ctx::MinAppContext;
|
||||
pub use settings::Settings;
|
||||
|
||||
pub const CACHE_AGE: u32 = 604800;
|
||||
|
@ -48,10 +51,19 @@ mod utils;
|
|||
//
|
||||
//#[derive(Parser)]
|
||||
//#[clap(author, version, about, long_about = None)]
|
||||
//struct Cli {
|
||||
// #[clap(subcommand)]
|
||||
// command: Commands,
|
||||
//}
|
||||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
Daemon,
|
||||
Verify { path: PathBuf },
|
||||
Test { path: PathBuf },
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
|
@ -61,16 +73,62 @@ async fn main() -> std::io::Result<()> {
|
|||
}
|
||||
|
||||
pretty_env_logger::init();
|
||||
// let cli = Cli::parse();
|
||||
let cli = Cli::parse();
|
||||
|
||||
info!(
|
||||
"{}: {}.\nFor more information, see: {}\nBuild info:\nVersion: {} commit: {}",
|
||||
PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, GIT_COMMIT_HASH
|
||||
);
|
||||
|
||||
match cli.command {
|
||||
Command::Daemon => run_daemon().await,
|
||||
Command::Verify { path } => {
|
||||
let s = std::fs::read_to_string(path).unwrap();
|
||||
let _: complaince::target::Target = toml::from_str(&s).unwrap();
|
||||
println!("Syntax OK");
|
||||
Ok(())
|
||||
}
|
||||
Command::Test { path } => {
|
||||
let ctx: Arc<dyn MinAppContext> = Arc::new(CliCtx::new());
|
||||
let serv = basic_server(ctx.clone()).await;
|
||||
|
||||
loop {
|
||||
log::info!("Waiting for server to start...");
|
||||
let res = reqwest::get(&format!(
|
||||
"http://localhost:29130{}",
|
||||
api::v1::meta::META.build_details
|
||||
))
|
||||
.await;
|
||||
if res.is_ok() {
|
||||
log::info!("Waiting server started");
|
||||
break;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::new(2, 0)).await;
|
||||
}
|
||||
|
||||
crate::runner::suite::SuiteRunnerState::run_proxy(ctx.as_ref());
|
||||
let (suite_results, init_containers) =
|
||||
crate::runner::target::run_target(ctx.as_ref(), path.clone()).await;
|
||||
let content = crate::runner::results::ArchivableResult {
|
||||
commit: "".into(),
|
||||
suites: suite_results,
|
||||
init_containers,
|
||||
};
|
||||
let results_file = path.join("resuts.json");
|
||||
println!("Writing results to: {:?}", path.canonicalize());
|
||||
std::fs::write(results_file, serde_json::to_string(&content).unwrap()).unwrap();
|
||||
|
||||
crate::runner::suite::SuiteRunnerState::stop_proxy(ctx.as_ref());
|
||||
serv.stop(true).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_daemon() -> std::io::Result<()> {
|
||||
let settings = Settings::new().unwrap();
|
||||
settings.init();
|
||||
let inner_ctx = Arc::new(Ctx::new(settings.clone()).await);
|
||||
let inner_ctx = Arc::new(DaemonCtx::new(settings.clone()).await);
|
||||
let ctx = AppFullCtx::new(inner_ctx.clone());
|
||||
let ctx2 = AppMinCtx::new(inner_ctx);
|
||||
ctx.db().migrate().await.unwrap();
|
||||
|
@ -79,14 +137,40 @@ async fn main() -> std::io::Result<()> {
|
|||
crate::runner::suite::SuiteRunnerState::run_proxy(ctx.as_ref());
|
||||
}
|
||||
|
||||
serve(ctx.clone(), ctx2).await?;
|
||||
daemon(ctx.clone(), ctx2).await?;
|
||||
if ctx.settings().docker_proxy {
|
||||
crate::runner::suite::SuiteRunnerState::stop_proxy(ctx.as_ref());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn serve(ctx: AppFullCtx, ctx2: AppMinCtx) -> std::io::Result<()> {
|
||||
async fn basic_server(ctx: Arc<dyn MinAppContext>) -> actix_web::dev::ServerHandle {
|
||||
let ctx = AppMinCtx::new(ctx);
|
||||
info!("Starting server on: http://0.0.0.0:29130");
|
||||
let serv = HttpServer::new(move || {
|
||||
App::new()
|
||||
.wrap(TracingLogger::default())
|
||||
.wrap(actix_middleware::Compress::default())
|
||||
.app_data(ctx.clone())
|
||||
.app_data(get_json_err())
|
||||
.wrap(
|
||||
actix_middleware::DefaultHeaders::new()
|
||||
.add(("Permissions-Policy", "interest-cohort=()")),
|
||||
)
|
||||
.wrap(actix_middleware::NormalizePath::new(
|
||||
actix_middleware::TrailingSlash::Trim,
|
||||
))
|
||||
.configure(services)
|
||||
})
|
||||
.bind("0.0.0.0:29130")
|
||||
.unwrap()
|
||||
.run();
|
||||
let handle = serv.handle();
|
||||
tokio::spawn(serv);
|
||||
handle
|
||||
}
|
||||
|
||||
async fn daemon(ctx: AppFullCtx, ctx2: AppMinCtx) -> std::io::Result<()> {
|
||||
let ip = ctx.settings().server.get_ip();
|
||||
let workers = ctx.settings().server.workers.unwrap_or_else(num_cpus::get);
|
||||
let scheduler = runner::scheduler::Scheduler::spawn(ctx.clone()).await;
|
||||
|
|
|
@ -59,14 +59,14 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
use crate::docker_compose::DockerCompose;
|
||||
use crate::{AppMinCtx, Ctx, Settings};
|
||||
use crate::{AppMinCtx, DaemonCtx, Settings};
|
||||
|
||||
use crate::complaince::suite::Test;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn launch_init_containers_works() {
|
||||
let settings = Settings::new().unwrap();
|
||||
let ctx = AppMinCtx::new(Arc::new(Ctx::new(settings.clone()).await));
|
||||
let ctx = AppMinCtx::new(Arc::new(DaemonCtx::new(settings.clone()).await));
|
||||
// let base_dir = Path::new(&ctx.settings.repository.base_dir);
|
||||
// let control = base_dir.join("control");
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ impl SuiteRunnerState {
|
|||
&format!("ftest_backend:{default}"),
|
||||
"forgeflux/ftest-nginx-proxy",
|
||||
];
|
||||
let mut child = std::process::Command::new("docker_")
|
||||
let mut child = std::process::Command::new("docker")
|
||||
.args(&args)
|
||||
.spawn()
|
||||
.expect("unable to obtain docker_ version");
|
||||
|
@ -172,7 +172,7 @@ mod tests {
|
|||
|
||||
use crate::complaince::result::Result as CResult;
|
||||
use crate::complaince::suite::Test;
|
||||
use crate::{AppMinCtx, Ctx, Settings};
|
||||
use crate::{AppMinCtx, DaemonCtx, Settings};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -240,7 +240,7 @@ mod tests {
|
|||
const LOGS: &str = "SUITE RUNNER LOG STRING";
|
||||
|
||||
let settings = Settings::new().unwrap();
|
||||
let ctx = Ctx::new(settings.clone()).await;
|
||||
let ctx = DaemonCtx::new(settings.clone()).await;
|
||||
// ctx.docker_ = Arc::new(Testdocker_::new());
|
||||
let ctx = crate::AppFullCtx::new(Arc::new(ctx));
|
||||
|
||||
|
|
Loading…
Reference in New Issue