feat: launch many services somewhat at once

This commit is contained in:
Christoph J. Scherr 2024-09-08 02:59:58 +02:00
parent f945eea904
commit ea23e93ec9
7 changed files with 99 additions and 56 deletions

View File

@ -8,7 +8,7 @@ use libpt::log::warn;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpListener;
use super::{Challenge, ChallengeDesc};
use super::{ChallengeDesc, ChallengeLike};
use crate::config::Config;
use crate::has_won;
use crate::vault::VaultRef;
@ -21,7 +21,7 @@ pub struct C1 {
}
#[async_trait]
impl Challenge for C1 {
impl ChallengeLike for C1 {
fn config(&self) -> Config {
self.config.clone()
}
@ -34,6 +34,7 @@ impl Challenge for C1 {
fn text() -> ChallengeDesc {
ChallengeDesc {
id: 1,
title: "dumb TCP".to_string(),
hints: vec![String::from("TCP connect to 1337.")],
solution: String::from("Connect by TCP, then the secret will be sent to you."),

View File

@ -10,7 +10,7 @@ use libpt::log::{info, warn};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use super::{Challenge, ChallengeDesc};
use super::{ChallengeDesc, ChallengeLike};
use crate::config::Config;
use crate::has_won;
use crate::vault::VaultRef;
@ -47,7 +47,7 @@ impl C2 {
}
#[async_trait]
impl Challenge for C2 {
impl ChallengeLike for C2 {
fn config(&self) -> Config {
self.config.clone()
}
@ -59,6 +59,7 @@ impl Challenge for C2 {
}
fn text() -> ChallengeDesc {
ChallengeDesc {
id: 2,
title: "TCP dialogue".to_string(),
hints: vec![String::from(
"TCP connect to 1337 and give me a special u16",

View File

@ -29,7 +29,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::{TcpListener, TcpStream};
use tokio::time::Instant;
use super::{Challenge, ChallengeDesc};
use super::{ChallengeDesc, ChallengeLike};
use crate::config::Config;
use crate::has_won;
use crate::vault::VaultRef;
@ -207,7 +207,7 @@ impl C3 {
}
#[async_trait]
impl Challenge for C3 {
impl ChallengeLike for C3 {
fn config(&self) -> Config {
self.config.clone()
}
@ -219,6 +219,7 @@ impl Challenge for C3 {
}
fn text() -> ChallengeDesc {
ChallengeDesc {
id: 3,
title: "TCP math exam".to_string(),
hints: vec![
"TCP connect to 1337 and answer the questions.".to_string(),

View File

@ -16,6 +16,7 @@ pub mod c3;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct ChallengeDesc {
id: usize,
title: String,
hints: Vec<String>,
solution: String,
@ -30,7 +31,7 @@ impl ChallengeDesc {
///
/// # Returns
///
/// A vector of strings containing hints for the challenge.
/// A [`Vec<String>`] containing hints for the challenge.
pub fn hints(&self) -> Vec<&str> {
self.hints.iter().map(|a| a.as_ref()).collect()
}
@ -41,7 +42,7 @@ impl ChallengeDesc {
///
/// # Returns
///
/// A string containing the solution to the challenge.
/// A [`String`] containing the solution to the challenge.
pub fn solution(&self) -> &str {
&self.solution
}
@ -52,7 +53,7 @@ impl ChallengeDesc {
///
/// # Returns
///
/// A string containing the description to the challenge.
/// A [`String`] containing the description to the challenge.
pub fn description(&self) -> &str {
&self.description
}
@ -62,17 +63,27 @@ impl ChallengeDesc {
///
/// # Returns
///
/// A string containing the title to the challenge.
/// A [`String`] containing the title to the challenge.
pub fn title(&self) -> &str {
&self.title
}
/// Returns the id to the challenge.
///
/// The id is just the index of the challenge to distinguish them easily
///
/// # Returns
///
/// A [`usize`] containing the title to the challenge.
pub fn id(&self) -> usize {
self.id
}
}
/// Defines the behavior of a challenge.
///
/// Any type that implements this trait can be served as a challenge in the Wooly Vault application.
#[async_trait]
pub trait Challenge
pub trait ChallengeLike
where
Self: Sized + 'static,
Self: Send,
@ -127,6 +138,7 @@ where
async fn start(&self) -> anyhow::Result<()> {
let c = self.clone();
tokio::spawn(async move {
info!("starting C{}", Self::text().id());
if let Err(e) = c.serve().await {
error!("challenge {} has crashed! {e:#?}", Self::text().title());
};
@ -134,34 +146,3 @@ where
Ok(())
}
}
/// Selects a challenge by index and serves it with the given configuration and vault.
///
/// # Arguments
///
/// * `index` - The index of the challenge to select.
/// * `config` - The configuration for the challenge.
/// * `vault` - The vault that holds the secret for the challenge.
///
/// # Returns
///
/// A result indicating whether the challenge has successfully ended.
///
/// # Errors
///
/// Returns an error if no challenge with the given index exists, or if the challenge that is being
/// served errors.
pub async fn select_and_start(index: u16, config: Config, vault: VaultRef) -> anyhow::Result<()> {
info!("select+start");
match index {
1 => c1::C1::new(config, vault).start().await?,
2 => c2::C2::new(config, vault).start().await?,
3 => c3::C3::new(config, vault).start().await?,
_ => {
return Err(anyhow!(
"no challenge with index {index} does currently exist"
))
}
}
Ok(())
}

View File

@ -14,6 +14,12 @@
//! Wooly Vault is programmed asynchronously with [tokio] to be able to handle many contestants at
//! once if needed.
use anyhow::anyhow;
use libpt::log::info;
use self::challenge::ChallengeLike;
use self::config::Config;
use self::meta::Service;
use self::vault::VaultRef;
pub mod challenge;
@ -31,3 +37,61 @@ pub(crate) async fn has_won(
vault.add_winner(contestant.clone()).await;
libpt::log::info!("Sending the secret to {addr}")
}
/// Selects a single challenge by index and serves it with the given configuration and vault.
///
/// # Arguments
///
/// * `index` - The index of the challenge to select.
/// * `config` - The configuration for the challenge.
/// * `vault` - The vault that holds the secret for the challenge.
///
/// # Returns
///
/// A result indicating whether the challenge has successfully ended.
///
/// # Errors
///
/// Returns an error if no challenge with the given index exists, or if the challenge that is being
/// served errors.
pub async fn select_and_start_single(
index: u16,
config: Config,
vault: VaultRef,
) -> anyhow::Result<()> {
info!("select+start");
match index {
1 => challenge::c1::C1::new(config, vault).start().await?,
2 => challenge::c2::C2::new(config, vault).start().await?,
3 => challenge::c3::C3::new(config, vault).start().await?,
_ => {
return Err(anyhow!(
"no challenge with index {index} does currently exist"
))
}
}
Ok(())
}
pub async fn start_all(config: Config, vault: VaultRef) -> anyhow::Result<()> {
let c1 = challenge::c1::C1::new(config.clone(), vault.clone());
let c2 = challenge::c2::C2::new(config.clone(), vault.clone());
let c3 = challenge::c3::C3::new(config.clone(), vault.clone());
c1.start().await?;
c2.start().await?;
c3.start().await?;
meta::serve(
vec![
challenge::c1::C1::text(),
challenge::c2::C2::text(),
challenge::c3::C3::text(),
],
vault.clone(),
config.clone(),
)
.await?;
Ok(())
}

View File

@ -2,7 +2,8 @@ use anyhow::Result;
use clap::Parser;
use libpt::log::{debug, info};
use wooly_vault::{challenge::select_and_start, config::Config, vault::Vault};
use wooly_vault::{config::Config, vault::Vault};
use wooly_vault::{select_and_start_single, start_all};
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
@ -21,7 +22,7 @@ async fn main() -> Result<()> {
let secret = Config::secret()?;
let v = Vault::new(&secret);
select_and_start(conf.challenge, conf, v).await?;
start_all(conf, v).await?;
loop {
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await

View File

@ -26,27 +26,21 @@ pub struct Service<'tp> {
challenges: Vec<ChallengeDesc>,
}
impl<'tp> Service<'tp> {
fn new(
vault: VaultRef,
config: Config,
env: Environment<'tp>,
challenges: Vec<ChallengeDesc>,
) -> Arc<Self> {
Self {
fn build(vault: VaultRef, config: Config, challenges: Vec<ChallengeDesc>) -> Result<Arc<Self>> {
let mut env = Environment::new();
env.add_template("index", include_str!("../../data/www/admin.html"))?;
Ok(Self {
vault,
config,
env,
challenges,
}
.into()
.into())
}
}
pub async fn serve(challenges: Vec<ChallengeDesc>, vault: VaultRef, config: Config) -> Result<()> {
let mut env = Environment::new();
env.add_template("index", include_str!("../../data/www/admin.html"))?;
let service = Service::new(vault, config, env, challenges);
let service = Service::build(vault, config, challenges)?;
let routes = Service::admin_routes(service.clone())
.or(Service::ressources_routes())