feat: Added http server that serve the same files

This commit is contained in:
2026-10-20 02:56:39 +02:00
parent a5594c768d
commit 620f36d0bb
7 changed files with 210 additions and 38 deletions
+2
View File
@@ -52,6 +52,8 @@ pub enum Commands {
ShellCompletions,
/// Serve config files over tftp
Serve {
#[arg(short, long, default_value = "3000")]
port: u16,
#[arg(long, default_value = "0.0.0.0")]
ip: IpAddr,
},
+10
View File
@@ -0,0 +1,10 @@
use crete::tftp::ServerError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("No clusters where found")]
NoClustersFound,
#[error("Server error: {0}")]
ServerError(#[from] ServerError),
}
+37
View File
@@ -0,0 +1,37 @@
use crete::cluster::Cluster;
use ipxe::IPXE_DIR;
use regex::Regex;
// TODO: Make this a proper async function
pub fn get_file(filename: impl ToString) -> impl Future<Output = Option<(Vec<u8>, &'static str)>> {
// TODO: Make this a static that is initialized once
let re =
Regex::new(r"^talos/(?<cluster>.+)/(?<node>.+)\.ya?ml$").expect("regex should be valid");
let filename: String = filename.to_string();
let re = re.clone();
async move {
const IPXE_PATTERN: &str = "ipxe/";
if let Some(filename) = filename.strip_prefix(IPXE_PATTERN) {
Some((
IPXE_DIR.get_file(filename)?.contents().to_owned(),
"application/octet-stream",
))
} else if let Some(caps) = re.captures(&filename) {
let cluster = &caps["cluster"];
let node = &caps["node"];
let clusters = Cluster::get_all().await;
let cluster = clusters.get(cluster)?;
let node = cluster.node(node)?;
let config = node.config(cluster).await;
Some((config.into(), "application/yaml; charset=utf-8"))
} else {
None
}
}
}
+31
View File
@@ -0,0 +1,31 @@
use std::path::PathBuf;
use axum::debug_handler;
use axum::extract::Request;
use axum::response::IntoResponse;
use reqwest::{StatusCode, header};
use crate::file::get_file;
#[debug_handler]
pub async fn handler(req: Request) -> impl IntoResponse {
let Some(path) = req.uri().path().strip_prefix("/") else {
return StatusCode::BAD_REQUEST.into_response();
};
let Some((file, content_type)) = get_file(path).await else {
return StatusCode::NOT_FOUND.into_response();
};
let path = PathBuf::from(path);
let filename = path.file_name().unwrap();
let filename = filename.to_str().unwrap();
let content_type = [(header::CONTENT_TYPE, content_type)];
let disposition = [(
header::CONTENT_DISPOSITION,
format!("inline; filename=\"{filename}\""),
)];
(content_type, disposition, file).into_response()
}
+20 -38
View File
@@ -1,30 +1,27 @@
mod cli;
mod error;
mod file;
mod http;
use std::net::SocketAddr;
use std::pin::pin;
use axum::Router;
use axum::routing::get;
use clap::{CommandFactory, Parser};
use clap_complete::{Shell, generate as generate_complete};
use crete::cluster::Cluster;
use crete::environment::PathEnvironment;
use crete::talosconfig::{get_current_context, set_context};
use crete::tftp::ServerError;
use crete::{get_configs_path, get_repo_path, set_repo_path, tftp};
use futures::StreamExt;
use futures::future::join_all;
use ipxe::IPXE_DIR;
use minijinja::context;
use regex::Regex;
use thiserror::Error;
use crate::cli::{Cli, Commands, GlobalOpts};
#[derive(Debug, Error)]
enum Error {
#[error("No clusters where found")]
NoClustersFound,
#[error("Server error: {0}")]
ServerError(#[from] ServerError),
}
use crate::error::Error;
use crate::file::get_file;
use crate::http::handler;
async fn generate(opts: &GlobalOpts) -> Result<(), Error> {
let clusters = Cluster::get_all().await;
@@ -107,36 +104,21 @@ async fn main() -> Result<(), Error> {
"crete",
&mut std::io::stdout(),
),
Commands::Serve { ip } => {
let re = Regex::new(r"talos/(?<cluster>.+)/(?<node>.+)\.ya?ml")
.expect("regex should be valid");
Commands::Serve { port, ip } => {
tokio::spawn(async move {
let app = Router::new().fallback_service(get(handler));
let addr: SocketAddr = (ip, port).into();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
println!("Http server started on {addr}");
axum::serve(listener, app).await.unwrap();
});
tftp::serve(ip, move |filename, _| {
let filename = filename.to_owned();
let re = re.clone();
async move {
const IPXE_PATTERN: &str = "ipxe/";
if let Some(filename) = filename.strip_prefix(IPXE_PATTERN) {
Some(IPXE_DIR.get_file(filename)?.contents().to_owned())
} else if let Some(caps) = re.captures(&filename) {
let cluster = &caps["cluster"];
let node = &caps["node"];
let clusters = Cluster::get_all().await;
let cluster = clusters.get(cluster)?;
let node = cluster.node(node)?;
let config = node.config(cluster).await;
Some(config.into())
} else {
None
}
}
async move { get_file(filename).await.map(|(file, _)| file) }
})
.await?
.await?;
}
};