feat(homelab): start implementing route generation

This commit is contained in:
2025-12-28 01:58:34 -08:00
parent d1b81ce0db
commit 0993820675
16 changed files with 879 additions and 49 deletions

View File

@@ -0,0 +1,9 @@
pub mod generate_routes;
use clap::Subcommand;
#[derive(Subcommand, Debug)]
pub enum Commands {
/// generate gateway api routes
GenerateRoutes,
}

View File

@@ -0,0 +1,72 @@
use serde::{Deserialize, Serialize};
use crate::{Config, HelperError};
#[derive(Serialize, Deserialize, Default)]
pub struct Route {
name: String,
hostname: String,
namespace: String,
service: Option<String>,
port: i16,
private: bool,
}
pub fn generate_routes(config: &Config) -> Result<(), HelperError> {
let routes = config
.routes
.iter()
.enumerate()
.fold(String::new(), |mut acc, (i, r)| {
if i > 0 {
acc.push_str("\n---\n");
}
acc.push_str(&generate_route(r));
acc
});
std::fs::write("kustomize/routes.yaml", &routes)?;
println!("Wrote: {}", routes);
Ok(())
}
fn generate_route(route: &Route) -> String {
let mut filters_section = String::new();
if route.private {
filters_section = format!(
r#"filters:
- type: ExtensionRef
extensionRef:
group: traefik.io
kind: Middleware
name: private-networks"#
);
};
format!(
r#"apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: {}
namespace: {}
spec:
parentRefs:
- name: traefik-gateway
namespace: kube-system
hostnames:
- {}.lucalise.ca
rules:
- backendRefs:
- name: {}
port: {}
{}"#,
route.name,
route.namespace,
route.hostname,
route.service.clone().unwrap_or_else(|| route.name.clone()),
route.port,
filters_section
)
.trim_end()
.to_string()
}

55
nix/homelab/src/main.rs Normal file
View File

@@ -0,0 +1,55 @@
mod commands;
use std::path::Path;
use anyhow::Context;
use clap::{CommandFactory, Parser};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::commands::{
Commands,
generate_routes::{Route, generate_routes},
};
#[derive(Parser, Debug)]
#[command(version = "0.1.0", about = "Helper for k3s", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Debug, Error)]
pub enum HelperError {
#[error("error reading file")]
ReadFile(#[from] std::io::Error),
#[error("error parsing config toml")]
TomlError(#[from] toml::de::Error),
}
#[derive(Serialize, Deserialize)]
pub struct Config {
routes: Vec<Route>,
}
pub fn parse_config<T: AsRef<Path>>(path: T) -> anyhow::Result<Config> {
let bytes = std::fs::read(&path).context(format!(
"failed to read config file: {}",
path.as_ref().display()
))?;
Ok(toml::from_slice::<Config>(&bytes)?)
}
fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match &cli.command {
Some(Commands::GenerateRoutes {}) => {
let config = parse_config("./config.toml")?;
generate_routes(&config)?;
}
None => Cli::command().print_long_help()?,
}
Ok(())
}