Skip to content

Commit

Permalink
Merge pull request #16 from DioCrafts/feature/frontend-helm-chart
Browse files Browse the repository at this point in the history
Feature/frontend helm chart
  • Loading branch information
DioCrafts authored Nov 19, 2024
2 parents 0962046 + 26af028 commit 7e6c0c9
Show file tree
Hide file tree
Showing 38 changed files with 1,536 additions and 1,248 deletions.
88 changes: 88 additions & 0 deletions api-gateway/backend/src/backends/handlers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//! Handlers para la gestión de backends en el API Gateway.
use actix_web::{web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
use std::sync::Mutex;

// Estructura del backend
#[derive(Serialize, Deserialize, Clone)]
pub struct Backend {
pub id: u32,
pub name: String,
pub address: String,
pub port: u16,
pub status: String, // Healthy, Unhealthy, Degraded
pub weight: Option<u32>, // Para balanceo de carga ponderado
}

// Estado compartido del servidor
pub struct AppState {
pub backends: Mutex<Vec<Backend>>,
}

// Listar todos los backends
pub async fn list_backends(data: web::Data<AppState>) -> impl Responder {
let backends = data.backends.lock().unwrap();
HttpResponse::Ok().json(&*backends)
}

// Agregar un nuevo backend
pub async fn add_backend(data: web::Data<AppState>, backend: web::Json<Backend>) -> impl Responder {
let mut backends = data.backends.lock().unwrap();
let mut new_backend = backend.into_inner();
// Generar ID único para el nuevo backend
new_backend.id = backends.last().map_or(1, |b| b.id + 1);
new_backend.status = "Unknown".to_string(); // Inicialmente desconocido
backends.push(new_backend);
HttpResponse::Created().finish()
}

// Actualizar un backend existente
pub async fn update_backend(
data: web::Data<AppState>,
id: web::Path<u32>,
updated_backend: web::Json<Backend>,
) -> impl Responder {
let mut backends = data.backends.lock().unwrap();
let id = id.into_inner();
if let Some(backend) = backends.iter_mut().find(|b| b.id == id) {
backend.name = updated_backend.name.clone();
backend.address = updated_backend.address.clone();
backend.port = updated_backend.port;
backend.weight = updated_backend.weight;
HttpResponse::Ok().finish()
} else {
HttpResponse::NotFound().finish()
}
}

// Eliminar un backend por ID
pub async fn delete_backend(data: web::Data<AppState>, id: web::Path<u32>) -> impl Responder {
let mut backends = data.backends.lock().unwrap();
let id = id.into_inner();
let initial_len = backends.len();
backends.retain(|backend| backend.id != id);
if backends.len() < initial_len {
HttpResponse::Ok().finish()
} else {
HttpResponse::NotFound().finish()
}
}

// Health Check de los backends
pub async fn health_check(data: web::Data<AppState>) -> impl Responder {
let mut backends = data.backends.lock().unwrap();
for backend in backends.iter_mut() {
let url = format!("http://{}:{}", backend.address, backend.port);
if let Ok(response) = reqwest::get(&url).await {
backend.status = if response.status().is_success() {
"Healthy".to_string()
} else {
"Unhealthy".to_string()
};
} else {
backend.status = "Unhealthy".to_string();
}
}
HttpResponse::Ok().json(&*backends)
}
11 changes: 11 additions & 0 deletions api-gateway/backend/src/backends/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pub mod handlers;
pub mod routes;

pub use routes::configure_routes;

/// Inicializar el manejador de backends
pub async fn start_backends_manager() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Starting Backends Manager...");
// Aquí iría la lógica específica de inicialización del manejador de backends
Ok(())
}
13 changes: 13 additions & 0 deletions api-gateway/backend/src/backends/routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use actix_web::web;
use super::handlers;

pub fn configure_routes(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/api/backends")
.route("", web::get().to(handlers::list_backends))
.route("", web::post().to(handlers::add_backend))
.route("/{id}", web::put().to(handlers::update_backend))
.route("/{id}", web::delete().to(handlers::delete_backend))
.route("/health-check", web::get().to(handlers::health_check)),
);
}
155 changes: 155 additions & 0 deletions api-gateway/backend/src/gateway/crd/gateway.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//! src/gateway/crd/gateway.rs
//!
//! Gestión de los recursos Gateway en Kubernetes.
//!
//! Este módulo implementa el `GatewayManager`, que proporciona métodos para
//! crear, listar, eliminar y configurar Gateways como CRDs.
use kube::{Api, Client, CustomResourceExt};
use kube::api::{PostParams, DeleteParams, ListParams, ApiResource, DynamicObject};
use serde::{Deserialize, Serialize};
use schemars::JsonSchema;
use kube_derive::CustomResource;
use thiserror::Error;
use kube::core::GroupVersionKind;

/// Especificación interna para el recurso Gateway.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GatewayInnerSpec {
pub hostname: String, // Nombre del host
pub tls_enabled: bool, // TLS habilitado o no
pub certificate: Option<String>, // Certificado TLS (si aplica)
pub routes: Vec<Route>, // Lista de rutas asociadas al Gateway
}

/// Definición de rutas asociadas al Gateway.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Route {
pub path: String, // Path como "/api/v1/*" o expresiones regulares
pub backend: String, // Backend al que se enruta
pub methods: Vec<String>, // Métodos HTTP permitidos (GET, POST, etc.)
}

/// Definición del CRD `Gateway`.
#[derive(CustomResource, Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[kube(
group = "networking.k8s.io", // Grupo del CRD
version = "v1alpha1", // Versión del API
kind = "Gateway", // Tipo de recurso
namespaced, // Es un recurso namespaced
)]
pub struct GatewaySpec {
pub spec: GatewayInnerSpec, // Especificación principal del Gateway
}

/// Manager para interactuar con los recursos Gateway en Kubernetes.
pub struct GatewayManager {
client: Client, // Cliente de Kubernetes
ar: ApiResource, // ApiResource que define los detalles del recurso DynamicObject
}

impl GatewayManager {
/// Crear una nueva instancia del `GatewayManager`.
pub fn new(client: Client) -> Self {
// Definir el ApiResource para el recurso Gateway.
let gvk = GroupVersionKind::gvk("networking.k8s.io", "v1alpha1", "Gateway");
let ar = ApiResource::from_gvk(&gvk);

Self { client, ar }
}

/// Listar todos los Gateways en el namespace especificado.
pub async fn list_gateways(&self, namespace: &str) -> Result<Vec<DynamicObject>, GatewayError> {
let gateways: Api<DynamicObject> = Api::namespaced_with(self.client.clone(), namespace, &self.ar);
let lp = ListParams::default();
let gateway_list = gateways.list(&lp).await?;
Ok(gateway_list.items)
}

/// Crear un nuevo Gateway.
pub async fn create_gateway(
&self,
namespace: &str,
gateway: &GatewaySpec,
) -> Result<DynamicObject, GatewayError> {
let gateways: Api<DynamicObject> = Api::namespaced_with(self.client.clone(), namespace, &self.ar);
let pp = PostParams::default();

// Usar el hostname como nombre del recurso.
let name = gateway.spec.hostname.clone();

// Crear un DynamicObject con la información del Gateway.
let mut crd = DynamicObject::new(&name, &self.ar);
crd.data = serde_json::Value::Object(
serde_json::to_value(gateway)?.as_object().cloned().unwrap_or_default()
);

let created_gateway = gateways.create(&pp, &crd).await?;
Ok(created_gateway)
}

/// Eliminar un Gateway por nombre.
pub async fn delete_gateway(&self, namespace: &str, name: &str) -> Result<(), GatewayError> {
let gateways: Api<DynamicObject> = Api::namespaced_with(self.client.clone(), namespace, &self.ar);
gateways.delete(name, &DeleteParams::default()).await?;
Ok(())
}

/// Configurar TLS para un Gateway.
pub async fn configure_tls(
&self,
namespace: &str,
name: &str,
certificate: &str,
) -> Result<(), GatewayError> {
let gateways: Api<DynamicObject> = Api::namespaced_with(self.client.clone(), namespace, &self.ar);

// Obtener el Gateway existente.
let mut existing_gateway = gateways.get(name).await?;
let mut data = existing_gateway.data.clone(); // Clonar el campo data

// Actualizar el certificado y habilitar TLS.
if let Some(spec) = data.get_mut("spec") {
if let Some(spec_obj) = spec.as_object_mut() {
spec_obj.insert("tlsEnabled".to_string(), serde_json::Value::Bool(true));
spec_obj.insert("certificate".to_string(), serde_json::Value::String(certificate.to_string()));
}
}

// Reemplazar el Gateway con los cambios actualizados.
existing_gateway.data = data; // Actualizar el campo data después de la edición
gateways.replace(name, &PostParams::default(), &existing_gateway).await?;
Ok(())
}

/// Obtener métricas simuladas de los Gateways.
pub async fn get_metrics(&self) -> Result<Vec<(String, String, u64)>, GatewayError> {
// Ejemplo de métricas simuladas.
Ok(vec![
("gateway_1".to_string(), "latency".to_string(), 120),
("gateway_1".to_string(), "requests".to_string(), 1500),
])
}
}

/// Definición de errores específicos para los Gateways.
#[derive(Error, Debug)]
pub enum GatewayError {
#[error("Error en la API de Kubernetes: {0}")]
KubeError(#[from] kube::Error),
#[error("Error de serialización/deserialización: {0}")]
SerdeError(#[from] serde_json::Error),
}

/// Implementación de conversión de `models::Route` a `gateway::crd::gateway::Route`.
impl From<crate::gateway::models::Route> for Route {
fn from(route: crate::gateway::models::Route) -> Self {
Route {
path: route.path.unwrap_or_default(),
backend: route.backend,
methods: route.methods,
}
}
}
Loading

0 comments on commit 7e6c0c9

Please sign in to comment.