refactor: update K8s, CI, Docker, Prometheus, scripts, and FXT CLI for api rename
- K8s: rename api-gateway → api manifests, delete web-gateway, update network policies - CI: rename compile/deploy jobs, delete web-gateway jobs - Docker: rename service in compose files - Prometheus: update scrape targets and alert rules - Scripts: update binary references in build/test/cert scripts - FXT CLI: rename api_gateway_url → api_url (with serde alias for compat) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,7 @@ variables:
|
|||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
# CUDA_COMPUTE_CAP is set at runtime by detect_gpu() — L4=89, H100=90
|
# CUDA_COMPUTE_CAP is set at runtime by detect_gpu() — L4=89, H100=90
|
||||||
# Do NOT hardcode here; sccache is partitioned by compute cap below
|
# Do NOT hardcode here; sccache is partitioned by compute cap below
|
||||||
# Stable JWT secret for api_gateway tests (prevents env-var race between parallel tests)
|
# Stable JWT secret for api tests (prevents env-var race between parallel tests)
|
||||||
JWT_SECRET: "CiTestSecret_Kx7mP9nR2sW5vY8bC3fG6jH1kL4pQ7tZ0uN9dM5eV8xS2wT6yA4zB_64chars_min"
|
JWT_SECRET: "CiTestSecret_Kx7mP9nR2sW5vY8bC3fG6jH1kL4pQ7tZ0uN9dM5eV8xS2wT6yA4zB_64chars_min"
|
||||||
# Training data on block storage PVC (mounted at /mnt/training-data by runner)
|
# Training data on block storage PVC (mounted at /mnt/training-data by runner)
|
||||||
FOXHUNT_DATA_DIR: "/mnt/training-data/futures-baseline"
|
FOXHUNT_DATA_DIR: "/mnt/training-data/futures-baseline"
|
||||||
@@ -448,14 +448,14 @@ test:
|
|||||||
# Each service compiled independently with dependency-aware changes: filters.
|
# Each service compiled independently with dependency-aware changes: filters.
|
||||||
# sccache shared across jobs — incremental crate compilation is cached.
|
# sccache shared across jobs — incremental crate compilation is cached.
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
compile-api-gateway:
|
compile-api:
|
||||||
extends: .compile-service-base
|
extends: .compile-service-base
|
||||||
variables:
|
variables:
|
||||||
SERVICE_PACKAGE: api-gateway
|
SERVICE_PACKAGE: api
|
||||||
rules: &rules-api-gateway
|
rules: &rules-api
|
||||||
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
|
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
|
||||||
changes:
|
changes:
|
||||||
- services/api_gateway/**
|
- services/api/**
|
||||||
- crates/common/**
|
- crates/common/**
|
||||||
- crates/config/**
|
- crates/config/**
|
||||||
- crates/trading_engine/**
|
- crates/trading_engine/**
|
||||||
@@ -572,19 +572,6 @@ compile-data-acquisition-service:
|
|||||||
- Cargo.lock
|
- Cargo.lock
|
||||||
- if: $CI_PIPELINE_SOURCE == "web" || $CI_PIPELINE_SOURCE == "api"
|
- if: $CI_PIPELINE_SOURCE == "web" || $CI_PIPELINE_SOURCE == "api"
|
||||||
|
|
||||||
compile-web-gateway:
|
|
||||||
extends: .compile-service-base
|
|
||||||
variables:
|
|
||||||
SERVICE_PACKAGE: web-gateway
|
|
||||||
rules: &rules-web-gateway
|
|
||||||
- if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"
|
|
||||||
changes:
|
|
||||||
- crates/web-gateway/**
|
|
||||||
- crates/common/**
|
|
||||||
- Cargo.toml
|
|
||||||
- Cargo.lock
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "web" || $CI_PIPELINE_SOURCE == "api"
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Stage 3a: Compile training binaries (CPU node, CUDA stubs from ci-builder image)
|
# Stage 3a: Compile training binaries (CPU node, CUDA stubs from ci-builder image)
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
@@ -1637,12 +1624,12 @@ infra-drift-check:
|
|||||||
# Rules use YAML anchors shared with compile jobs — ensures compile+deploy
|
# Rules use YAML anchors shared with compile jobs — ensures compile+deploy
|
||||||
# are created/skipped together.
|
# are created/skipped together.
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
deploy-api-gateway:
|
deploy-api:
|
||||||
extends: .deploy-service-base
|
extends: .deploy-service-base
|
||||||
needs: [{job: compile-api-gateway, artifacts: true}]
|
needs: [{job: compile-api, artifacts: true}]
|
||||||
variables:
|
variables:
|
||||||
SERVICE_PACKAGE: api-gateway
|
SERVICE_PACKAGE: api
|
||||||
rules: *rules-api-gateway
|
rules: *rules-api
|
||||||
|
|
||||||
deploy-trading-service:
|
deploy-trading-service:
|
||||||
extends: .deploy-service-base
|
extends: .deploy-service-base
|
||||||
@@ -1686,13 +1673,6 @@ deploy-data-acquisition-service:
|
|||||||
SERVICE_PACKAGE: data-acquisition-service
|
SERVICE_PACKAGE: data-acquisition-service
|
||||||
rules: *rules-data-acquisition-service
|
rules: *rules-data-acquisition-service
|
||||||
|
|
||||||
deploy-web-gateway:
|
|
||||||
extends: .deploy-service-base
|
|
||||||
needs: [{job: compile-web-gateway, artifacts: true}]
|
|
||||||
variables:
|
|
||||||
SERVICE_PACKAGE: web-gateway
|
|
||||||
rules: *rules-web-gateway
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Stage 4b: Deploy training binaries to MinIO
|
# Stage 4b: Deploy training binaries to MinIO
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|||||||
111
Cargo.lock
generated
111
Cargo.lock
generated
@@ -250,82 +250,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api-gateway"
|
name = "api_load_tests"
|
||||||
version = "1.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"async-stream",
|
|
||||||
"async-trait",
|
|
||||||
"axum 0.7.9",
|
|
||||||
"base32",
|
|
||||||
"base64 0.22.1",
|
|
||||||
"bytes",
|
|
||||||
"chrono",
|
|
||||||
"clap",
|
|
||||||
"common",
|
|
||||||
"config",
|
|
||||||
"const-oid",
|
|
||||||
"criterion",
|
|
||||||
"dashmap 6.1.0",
|
|
||||||
"futures",
|
|
||||||
"fxt",
|
|
||||||
"governor",
|
|
||||||
"hdrhistogram",
|
|
||||||
"hex",
|
|
||||||
"hmac",
|
|
||||||
"http 1.3.1",
|
|
||||||
"http-body 1.0.1",
|
|
||||||
"http-body-util",
|
|
||||||
"hyper 1.7.0",
|
|
||||||
"hyper-util",
|
|
||||||
"image 0.25.8",
|
|
||||||
"jsonwebtoken",
|
|
||||||
"k8s-openapi 0.27.0",
|
|
||||||
"kube 3.0.1",
|
|
||||||
"lru",
|
|
||||||
"num-traits",
|
|
||||||
"ocsp",
|
|
||||||
"once_cell",
|
|
||||||
"prometheus",
|
|
||||||
"prost 0.14.1",
|
|
||||||
"prost-build",
|
|
||||||
"qrcode",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"redis",
|
|
||||||
"regex",
|
|
||||||
"reqwest 0.12.23",
|
|
||||||
"rust_decimal",
|
|
||||||
"rustls 0.23.32",
|
|
||||||
"secrecy 0.8.0",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sha1",
|
|
||||||
"sha2",
|
|
||||||
"sqlx",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tokio-test",
|
|
||||||
"tonic 0.14.2",
|
|
||||||
"tonic-health",
|
|
||||||
"tonic-prost",
|
|
||||||
"tonic-prost-build",
|
|
||||||
"tonic-reflection",
|
|
||||||
"totp-rs",
|
|
||||||
"tower 0.4.13",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"trading_engine",
|
|
||||||
"urlencoding",
|
|
||||||
"uuid",
|
|
||||||
"x509-parser",
|
|
||||||
"zeroize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "api_gateway_load_tests"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -1442,7 +1367,6 @@ checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core 0.4.5",
|
"axum-core 0.4.5",
|
||||||
"base64 0.22.1",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
@@ -1461,10 +1385,8 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_path_to_error",
|
"serde_path_to_error",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sha1",
|
|
||||||
"sync_wrapper 1.0.2",
|
"sync_wrapper 1.0.2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
|
||||||
"tower 0.5.2",
|
"tower 0.5.2",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -11359,7 +11281,7 @@ name = "trading-service"
|
|||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"api-gateway",
|
"api",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum 0.7.9",
|
"axum 0.7.9",
|
||||||
@@ -11999,35 +11921,6 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "web-gateway"
|
|
||||||
version = "1.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"axum 0.7.9",
|
|
||||||
"chrono",
|
|
||||||
"common",
|
|
||||||
"futures-util",
|
|
||||||
"jsonwebtoken",
|
|
||||||
"once_cell",
|
|
||||||
"prometheus",
|
|
||||||
"prost 0.14.1",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tokio-test",
|
|
||||||
"tonic 0.14.2",
|
|
||||||
"tonic-prost",
|
|
||||||
"tonic-prost-build",
|
|
||||||
"tower 0.4.13",
|
|
||||||
"tower-http 0.5.2",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.81"
|
version = "0.3.81"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! JWT Token Generation for FXT
|
//! JWT Token Generation for FXT
|
||||||
//!
|
//!
|
||||||
//! Provides JWT token generation for simulated authentication flows.
|
//! Provides JWT token generation for simulated authentication flows.
|
||||||
//! Uses the same configuration as API Gateway for compatibility.
|
//! Uses the same configuration as the unified API for compatibility.
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use jsonwebtoken::{encode, EncodingKey, Header};
|
use jsonwebtoken::{encode, EncodingKey, Header};
|
||||||
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// JWT claims structure matching API Gateway expectations
|
/// JWT claims structure matching API expectations
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct JwtClaims {
|
pub struct JwtClaims {
|
||||||
/// JWT ID (unique identifier for revocation)
|
/// JWT ID (unique identifier for revocation)
|
||||||
@@ -38,7 +38,7 @@ pub struct JwtClaims {
|
|||||||
pub session_id: Option<String>,
|
pub session_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// JWT configuration matching API Gateway
|
/// JWT configuration matching the unified API
|
||||||
pub struct JwtConfig {
|
pub struct JwtConfig {
|
||||||
pub secret: String,
|
pub secret: String,
|
||||||
pub issuer: String,
|
pub issuer: String,
|
||||||
@@ -62,7 +62,7 @@ impl Default for JwtConfig {
|
|||||||
Self {
|
Self {
|
||||||
secret: resolved_secret,
|
secret: resolved_secret,
|
||||||
issuer: std::env::var("JWT_ISSUER")
|
issuer: std::env::var("JWT_ISSUER")
|
||||||
.unwrap_or_else(|_| "foxhunt-api-gateway".to_owned()),
|
.unwrap_or_else(|_| "foxhunt-api".to_owned()),
|
||||||
audience: std::env::var("JWT_AUDIENCE")
|
audience: std::env::var("JWT_AUDIENCE")
|
||||||
.unwrap_or_else(|_| "foxhunt-services".to_owned()),
|
.unwrap_or_else(|_| "foxhunt-services".to_owned()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,9 @@ pub struct RefreshResponse {
|
|||||||
pub expires_at: u64,
|
pub expires_at: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Login client for API Gateway authentication
|
/// Login client for API authentication
|
||||||
pub struct LoginClient {
|
pub struct LoginClient {
|
||||||
/// gRPC channel to the API Gateway (used for future gRPC auth calls)
|
/// gRPC channel to the API (used for future gRPC auth calls)
|
||||||
gateway_channel: Channel,
|
gateway_channel: Channel,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +79,9 @@ impl LoginClient {
|
|||||||
&self.gateway_channel
|
&self.gateway_channel
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether an API Gateway URL has been configured in the environment
|
/// Check whether an API URL has been configured in the environment
|
||||||
fn is_api_gateway_available() -> bool {
|
fn is_api_available() -> bool {
|
||||||
std::env::var("API_GATEWAY_URL").is_ok()
|
std::env::var("API_URL").is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform interactive login flow
|
/// Perform interactive login flow
|
||||||
@@ -108,11 +108,11 @@ impl LoginClient {
|
|||||||
// Attempt login
|
// Attempt login
|
||||||
let _login_request = LoginRequest { username, password };
|
let _login_request = LoginRequest { username, password };
|
||||||
|
|
||||||
// TODO: Call API Gateway login endpoint via gRPC
|
// TODO: Call API login endpoint via gRPC
|
||||||
// For now, simulate a successful login response
|
// For now, simulate a successful login response
|
||||||
if Self::is_api_gateway_available() {
|
if Self::is_api_available() {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"API Gateway configured but gRPC client not yet implemented -- using simulation"
|
"API configured but gRPC client not yet implemented -- using simulation"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
@@ -154,11 +154,11 @@ impl LoginClient {
|
|||||||
password: password.to_owned(),
|
password: password.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Call API Gateway login endpoint via gRPC
|
// TODO: Call API login endpoint via gRPC
|
||||||
// For now, simulate a successful login response
|
// For now, simulate a successful login response
|
||||||
if Self::is_api_gateway_available() {
|
if Self::is_api_available() {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"API Gateway configured but gRPC client not yet implemented -- using simulation"
|
"API configured but gRPC client not yet implemented -- using simulation"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
@@ -209,10 +209,10 @@ impl LoginClient {
|
|||||||
totp_code,
|
totp_code,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Call API Gateway MFA endpoint via gRPC
|
// TODO: Call API MFA endpoint via gRPC
|
||||||
if Self::is_api_gateway_available() {
|
if Self::is_api_available() {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"API Gateway configured but gRPC client not yet implemented -- using simulation"
|
"API configured but gRPC client not yet implemented -- using simulation"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
@@ -250,10 +250,10 @@ impl LoginClient {
|
|||||||
refresh_token: refresh_token.clone(),
|
refresh_token: refresh_token.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Call API Gateway refresh endpoint via gRPC
|
// TODO: Call API refresh endpoint via gRPC
|
||||||
if Self::is_api_gateway_available() {
|
if Self::is_api_available() {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"API Gateway configured but gRPC client not yet implemented -- using simulation"
|
"API configured but gRPC client not yet implemented -- using simulation"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ impl ConfigCommand {
|
|||||||
.unwrap_or_else(|| "~/.foxhunt/config.toml".to_owned());
|
.unwrap_or_else(|| "~/.foxhunt/config.toml".to_owned());
|
||||||
|
|
||||||
let result = EnvResult {
|
let result = EnvResult {
|
||||||
api_url: config.api_gateway_url,
|
api_url: config.api_url,
|
||||||
config_file: config_path,
|
config_file: config_path,
|
||||||
log_level: config.log_level,
|
log_level: config.log_level,
|
||||||
token_storage: config.token_storage,
|
token_storage: config.token_storage,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
//! ## Example Config File
|
//! ## Example Config File
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! # ~/.foxhunt/config.toml
|
//! # ~/.foxhunt/config.toml
|
||||||
//! api_gateway_url = "https://api.fxhnt.ai"
|
//! api_url = "https://api.fxhnt.ai"
|
||||||
//! log_level = "info"
|
//! log_level = "info"
|
||||||
//! token_storage = "keyring"
|
//! token_storage = "keyring"
|
||||||
//! ```
|
//! ```
|
||||||
@@ -85,9 +85,9 @@ impl Default for BrokerConfig {
|
|||||||
/// by CLI arguments or environment variables.
|
/// by CLI arguments or environment variables.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct FxtConfig {
|
pub struct FxtConfig {
|
||||||
/// API Gateway URL (default: <http://localhost:50051>)
|
/// Unified API URL (default: <https://api.fxhnt.ai>)
|
||||||
#[serde(default = "default_api_gateway_url")]
|
#[serde(default = "default_api_url", alias = "api_gateway_url")]
|
||||||
pub api_gateway_url: String,
|
pub api_url: String,
|
||||||
|
|
||||||
/// Log level (default: info)
|
/// Log level (default: info)
|
||||||
#[serde(default = "default_log_level")]
|
#[serde(default = "default_log_level")]
|
||||||
@@ -106,7 +106,7 @@ pub struct FxtConfig {
|
|||||||
pub broker: BrokerConfig,
|
pub broker: BrokerConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_api_gateway_url() -> String {
|
fn default_api_url() -> String {
|
||||||
"https://api.fxhnt.ai".to_owned()
|
"https://api.fxhnt.ai".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ fn default_token_storage() -> String {
|
|||||||
impl Default for FxtConfig {
|
impl Default for FxtConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
api_gateway_url: default_api_gateway_url(),
|
api_url: default_api_url(),
|
||||||
log_level: default_log_level(),
|
log_level: default_log_level(),
|
||||||
token_storage: default_token_storage(),
|
token_storage: default_token_storage(),
|
||||||
jwt_secret: None,
|
jwt_secret: None,
|
||||||
@@ -141,7 +141,7 @@ impl FxtConfig {
|
|||||||
/// use fxt::config::FxtConfig;
|
/// use fxt::config::FxtConfig;
|
||||||
///
|
///
|
||||||
/// let config = FxtConfig::load().unwrap();
|
/// let config = FxtConfig::load().unwrap();
|
||||||
/// println!("API Gateway: {}", config.api_gateway_url);
|
/// println!("API URL: {}", config.api_url);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn load() -> Result<Self> {
|
pub fn load() -> Result<Self> {
|
||||||
let config_path = Self::config_path()?;
|
let config_path = Self::config_path()?;
|
||||||
@@ -168,7 +168,7 @@ impl FxtConfig {
|
|||||||
/// use fxt::config::FxtConfig;
|
/// use fxt::config::FxtConfig;
|
||||||
///
|
///
|
||||||
/// let config = FxtConfig {
|
/// let config = FxtConfig {
|
||||||
/// api_gateway_url: "http://localhost:50051".to_string(),
|
/// api_url: "http://localhost:50051".to_string(),
|
||||||
/// log_level: "debug".to_string(),
|
/// log_level: "debug".to_string(),
|
||||||
/// token_storage: "keyring".to_string(),
|
/// token_storage: "keyring".to_string(),
|
||||||
/// jwt_secret: None,
|
/// jwt_secret: None,
|
||||||
@@ -210,7 +210,7 @@ mod tests {
|
|||||||
fn test_default_config() {
|
fn test_default_config() {
|
||||||
// Test that Default trait provides correct values
|
// Test that Default trait provides correct values
|
||||||
let config = FxtConfig::default();
|
let config = FxtConfig::default();
|
||||||
assert_eq!(config.api_gateway_url, "https://api.fxhnt.ai");
|
assert_eq!(config.api_url, "https://api.fxhnt.ai");
|
||||||
assert_eq!(config.log_level, "info");
|
assert_eq!(config.log_level, "info");
|
||||||
assert_eq!(config.token_storage, "keyring");
|
assert_eq!(config.token_storage, "keyring");
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_config_serialization() {
|
fn test_config_serialization() {
|
||||||
let config = FxtConfig {
|
let config = FxtConfig {
|
||||||
api_gateway_url: "http://example.com:50051".to_owned(),
|
api_url: "http://example.com:50051".to_owned(),
|
||||||
log_level: "debug".to_owned(),
|
log_level: "debug".to_owned(),
|
||||||
token_storage: "file".to_owned(),
|
token_storage: "file".to_owned(),
|
||||||
jwt_secret: None,
|
jwt_secret: None,
|
||||||
@@ -228,7 +228,7 @@ mod tests {
|
|||||||
let toml_str = toml::to_string(&config).unwrap();
|
let toml_str = toml::to_string(&config).unwrap();
|
||||||
let parsed: FxtConfig = toml::from_str(&toml_str).unwrap();
|
let parsed: FxtConfig = toml::from_str(&toml_str).unwrap();
|
||||||
|
|
||||||
assert_eq!(config.api_gateway_url, parsed.api_gateway_url);
|
assert_eq!(config.api_url, parsed.api_url);
|
||||||
assert_eq!(config.log_level, parsed.log_level);
|
assert_eq!(config.log_level, parsed.log_level);
|
||||||
assert_eq!(config.token_storage, parsed.token_storage);
|
assert_eq!(config.token_storage, parsed.token_storage);
|
||||||
}
|
}
|
||||||
@@ -246,15 +246,23 @@ mod tests {
|
|||||||
// Test that empty TOML string deserializes to defaults
|
// Test that empty TOML string deserializes to defaults
|
||||||
let empty_toml = "";
|
let empty_toml = "";
|
||||||
let config: FxtConfig = toml::from_str(empty_toml).unwrap();
|
let config: FxtConfig = toml::from_str(empty_toml).unwrap();
|
||||||
assert_eq!(config.api_gateway_url, "https://api.fxhnt.ai");
|
assert_eq!(config.api_url, "https://api.fxhnt.ai");
|
||||||
assert_eq!(config.log_level, "info");
|
assert_eq!(config.log_level, "info");
|
||||||
assert_eq!(config.token_storage, "keyring");
|
assert_eq!(config.token_storage, "keyring");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_backwards_compat_alias() {
|
||||||
|
// Old config files using api_gateway_url must still deserialize correctly.
|
||||||
|
let toml_str = r#"api_gateway_url = "http://legacy:50051""#;
|
||||||
|
let config: FxtConfig = toml::from_str(toml_str).unwrap();
|
||||||
|
assert_eq!(config.api_url, "http://legacy:50051");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_config_with_broker_section() {
|
fn test_config_with_broker_section() {
|
||||||
let toml_str = r#"
|
let toml_str = r#"
|
||||||
api_gateway_url = "http://localhost:50051"
|
api_url = "http://localhost:50051"
|
||||||
|
|
||||||
[broker]
|
[broker]
|
||||||
gateway_url = "http://broker-gw:50060"
|
gateway_url = "http://broker-gw:50060"
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ impl Cockpit for PodsCockpit {
|
|||||||
|
|
||||||
/// Shorten a K8s pod name to its trailing random suffix.
|
/// Shorten a K8s pod name to its trailing random suffix.
|
||||||
///
|
///
|
||||||
/// `api-gateway-7686d84bdd-zl4mn` becomes `zl4mn`.
|
/// `api-7686d84bdd-zl4mn` becomes `zl4mn`.
|
||||||
/// Falls back to the full name if there is no `-` separator.
|
/// Falls back to the full name if there is no `-` separator.
|
||||||
fn shorten_pod_name(name: &str) -> &str {
|
fn shorten_pod_name(name: &str) -> &str {
|
||||||
name.rsplit('-').next().unwrap_or(name)
|
name.rsplit('-').next().unwrap_or(name)
|
||||||
|
|||||||
@@ -1429,14 +1429,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn group_pods_groups_by_service() {
|
fn group_pods_groups_by_service() {
|
||||||
let pods = vec![
|
let pods = vec![
|
||||||
make_pod("api-1", "api-gateway", true),
|
make_pod("api-1", "api", true),
|
||||||
make_pod("api-2", "api-gateway", true),
|
make_pod("api-2", "api", true),
|
||||||
make_pod("trade-1", "trading-service", true),
|
make_pod("trade-1", "trading-service", true),
|
||||||
make_pod("trade-2", "trading-service", false),
|
make_pod("trade-2", "trading-service", false),
|
||||||
];
|
];
|
||||||
let groups = group_pods(pods);
|
let groups = group_pods(pods);
|
||||||
assert_eq!(groups.len(), 2);
|
assert_eq!(groups.len(), 2);
|
||||||
assert_eq!(groups[0].service, "api-gateway");
|
assert_eq!(groups[0].service, "api");
|
||||||
assert_eq!(groups[0].total_count, 2);
|
assert_eq!(groups[0].total_count, 2);
|
||||||
assert_eq!(groups[0].ready_count, 2);
|
assert_eq!(groups[0].ready_count, 2);
|
||||||
assert_eq!(groups[1].service, "trading-service");
|
assert_eq!(groups[1].service, "trading-service");
|
||||||
@@ -1448,14 +1448,14 @@ mod tests {
|
|||||||
fn group_pods_empty_service_becomes_other() {
|
fn group_pods_empty_service_becomes_other() {
|
||||||
let pods = vec![
|
let pods = vec![
|
||||||
make_pod("orphan-1", "", true),
|
make_pod("orphan-1", "", true),
|
||||||
make_pod("api-1", "api-gateway", true),
|
make_pod("api-1", "api", true),
|
||||||
];
|
];
|
||||||
let groups = group_pods(pods);
|
let groups = group_pods(pods);
|
||||||
assert_eq!(groups.len(), 2);
|
assert_eq!(groups.len(), 2);
|
||||||
// BTreeMap sorts alphabetically: "(other)" < "api-gateway"
|
// BTreeMap sorts alphabetically: "(other)" < "api"
|
||||||
assert_eq!(groups[0].service, "(other)");
|
assert_eq!(groups[0].service, "(other)");
|
||||||
assert_eq!(groups[0].total_count, 1);
|
assert_eq!(groups[0].total_count, 1);
|
||||||
assert_eq!(groups[1].service, "api-gateway");
|
assert_eq!(groups[1].service, "api");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1079,7 +1079,7 @@ mod tests {
|
|||||||
let mut state = AppState::default();
|
let mut state = AppState::default();
|
||||||
let services = vec![
|
let services = vec![
|
||||||
ServiceData {
|
ServiceData {
|
||||||
name: "api-gateway".into(),
|
name: "api".into(),
|
||||||
healthy: true,
|
healthy: true,
|
||||||
uptime: "5d".into(),
|
uptime: "5d".into(),
|
||||||
version: "v1.0".into(),
|
version: "v1.0".into(),
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ scrape_configs:
|
|||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['localhost:9090']
|
- targets: ['localhost:9090']
|
||||||
|
|
||||||
- job_name: 'api_gateway'
|
- job_name: 'api'
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['api_gateway:9091']
|
- targets: ['api:9091']
|
||||||
metrics_path: '/metrics'
|
metrics_path: '/metrics'
|
||||||
scrape_interval: 5s
|
scrape_interval: 5s
|
||||||
relabel_configs:
|
relabel_configs:
|
||||||
- source_labels: [__address__]
|
- source_labels: [__address__]
|
||||||
target_label: instance
|
target_label: instance
|
||||||
replacement: 'api_gateway'
|
replacement: 'api'
|
||||||
|
|
||||||
- job_name: 'trading_service'
|
- job_name: 'trading_service'
|
||||||
static_configs:
|
static_configs:
|
||||||
|
|||||||
@@ -10,20 +10,20 @@ groups:
|
|||||||
interval: 15s
|
interval: 15s
|
||||||
rules:
|
rules:
|
||||||
# P99 Latency > 100ms - CRITICAL
|
# P99 Latency > 100ms - CRITICAL
|
||||||
- alert: CriticalP99LatencyAPIGateway
|
- alert: CriticalP99LatencyAPI
|
||||||
expr: |
|
expr: |
|
||||||
histogram_quantile(0.99,
|
histogram_quantile(0.99,
|
||||||
rate(grpc_server_handling_seconds_bucket{job="api_gateway"}[1m])
|
rate(grpc_server_handling_seconds_bucket{job="api"}[1m])
|
||||||
) > 0.1
|
) > 0.1
|
||||||
for: 1m
|
for: 1m
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
component: latency
|
component: latency
|
||||||
service: api_gateway
|
service: api
|
||||||
annotations:
|
annotations:
|
||||||
summary: "CRITICAL: API Gateway P99 latency exceeds 100ms"
|
summary: "CRITICAL: API P99 latency exceeds 100ms"
|
||||||
description: |
|
description: |
|
||||||
API Gateway P99 latency is {{ $value | humanizeDuration }}
|
API P99 latency is {{ $value | humanizeDuration }}
|
||||||
Target: < 100ms
|
Target: < 100ms
|
||||||
Current: {{ $value | humanizeDuration }}
|
Current: {{ $value | humanizeDuration }}
|
||||||
This impacts all client requests.
|
This impacts all client requests.
|
||||||
@@ -69,7 +69,7 @@ groups:
|
|||||||
|
|
||||||
# Service Down - CRITICAL
|
# Service Down - CRITICAL
|
||||||
- alert: CriticalServiceDown
|
- alert: CriticalServiceDown
|
||||||
expr: up{job=~"api_gateway|trading_service|trading_agent_service"} == 0
|
expr: up{job=~"api|trading_service|trading_agent_service"} == 0
|
||||||
for: 30s
|
for: 30s
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
@@ -90,22 +90,22 @@ groups:
|
|||||||
interval: 15s
|
interval: 15s
|
||||||
rules:
|
rules:
|
||||||
# Error Rate > 1% - WARNING
|
# Error Rate > 1% - WARNING
|
||||||
- alert: HighErrorRateAPIGateway
|
- alert: HighErrorRateAPI
|
||||||
expr: |
|
expr: |
|
||||||
sum(rate(grpc_server_handled_total{job="api_gateway",grpc_code!="OK"}[5m]))
|
sum(rate(grpc_server_handled_total{job="api",grpc_code!="OK"}[5m]))
|
||||||
/
|
/
|
||||||
sum(rate(grpc_server_handled_total{job="api_gateway"}[5m])) > 0.01
|
sum(rate(grpc_server_handled_total{job="api"}[5m])) > 0.01
|
||||||
for: 3m
|
for: 3m
|
||||||
labels:
|
labels:
|
||||||
severity: warning
|
severity: warning
|
||||||
component: errors
|
component: errors
|
||||||
service: api_gateway
|
service: api
|
||||||
annotations:
|
annotations:
|
||||||
summary: "WARNING: API Gateway error rate exceeds 1%"
|
summary: "WARNING: API error rate exceeds 1%"
|
||||||
description: |
|
description: |
|
||||||
Error rate: {{ $value | humanizePercentage }}
|
Error rate: {{ $value | humanizePercentage }}
|
||||||
Target: < 1%
|
Target: < 1%
|
||||||
Total errors in last 5m: {{ with query "sum(increase(grpc_server_handled_total{job='api_gateway',grpc_code!='OK'}[5m]))" }}{{ . | first | value | humanize }}{{ end }}
|
Total errors in last 5m: {{ with query "sum(increase(grpc_server_handled_total{job='api',grpc_code!='OK'}[5m]))" }}{{ . | first | value | humanize }}{{ end }}
|
||||||
Review error logs immediately.
|
Review error logs immediately.
|
||||||
runbook_url: "https://wiki.foxhunt.local/runbooks/high-error-rate"
|
runbook_url: "https://wiki.foxhunt.local/runbooks/high-error-rate"
|
||||||
|
|
||||||
@@ -431,9 +431,9 @@ groups:
|
|||||||
- alert: DegradedSystemHealth
|
- alert: DegradedSystemHealth
|
||||||
expr: |
|
expr: |
|
||||||
(
|
(
|
||||||
sum(up{job=~"api_gateway|trading_service|backtesting_service|ml_training_service"})
|
sum(up{job=~"api|trading_service|backtesting_service|ml_training_service"})
|
||||||
/
|
/
|
||||||
count(up{job=~"api_gateway|trading_service|backtesting_service|ml_training_service"})
|
count(up{job=~"api|trading_service|backtesting_service|ml_training_service"})
|
||||||
) < 0.75
|
) < 0.75
|
||||||
for: 2m
|
for: 2m
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ groups:
|
|||||||
- name: foxhunt-service-health
|
- name: foxhunt-service-health
|
||||||
rules:
|
rules:
|
||||||
# Individual Service Health
|
# Individual Service Health
|
||||||
- alert: APIGatewayDown
|
- alert: APIDown
|
||||||
expr: up{job="api_gateway"} == 0
|
expr: up{job="api"} == 0
|
||||||
for: 1m
|
for: 1m
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
service: api_gateway
|
service: api
|
||||||
annotations:
|
annotations:
|
||||||
summary: "API Gateway is down"
|
summary: "API is down"
|
||||||
description: "API Gateway has been down for more than 1 minute. All client access is blocked."
|
description: "API has been down for more than 1 minute. All client access is blocked."
|
||||||
|
|
||||||
- alert: TradingServiceDown
|
- alert: TradingServiceDown
|
||||||
expr: up{job="trading_service"} == 0
|
expr: up{job="trading_service"} == 0
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
api_gateway:
|
api:
|
||||||
environment:
|
environment:
|
||||||
- JWT_SECRET=YZg5/mpqzH0NehGJXiR1yUgUg74HqdOUj/q9tnVSX+gqZvuzHKI1n0NhL4yP8CkUx7WyrVs3X86OSSxIUA6sxQ==
|
- JWT_SECRET=YZg5/mpqzH0NehGJXiR1yUgUg74HqdOUj/q9tnVSX+gqZvuzHKI1n0NhL4yP8CkUx7WyrVs3X86OSSxIUA6sxQ==
|
||||||
|
|
||||||
|
|||||||
@@ -426,11 +426,11 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# API Gateway - Auth + routing (port 50051)
|
# API Gateway - Auth + routing (port 50051)
|
||||||
api_gateway:
|
api:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: services/api_gateway/Dockerfile
|
dockerfile: services/api/Dockerfile
|
||||||
container_name: foxhunt-api-gateway
|
container_name: foxhunt-api
|
||||||
env_file:
|
env_file:
|
||||||
- .env # Load JWT_SECRET and other config from .env (Wave 147)
|
- .env # Load JWT_SECRET and other config from .env (Wave 147)
|
||||||
ports:
|
ports:
|
||||||
@@ -446,8 +446,9 @@ services:
|
|||||||
- BACKTESTING_SERVICE_URL=https://backtesting_service:50053
|
- BACKTESTING_SERVICE_URL=https://backtesting_service:50053
|
||||||
- ML_TRAINING_SERVICE_URL=https://ml_training_service:50053
|
- ML_TRAINING_SERVICE_URL=https://ml_training_service:50053
|
||||||
- JWT_SECRET=${JWT_SECRET:-dev_secret_key_change_in_production}
|
- JWT_SECRET=${JWT_SECRET:-dev_secret_key_change_in_production}
|
||||||
- JWT_ISSUER=foxhunt-api-gateway
|
- JWT_ISSUER=foxhunt-api
|
||||||
- JWT_AUDIENCE=foxhunt-services
|
- JWT_AUDIENCE=foxhunt-services
|
||||||
|
- CORS_ORIGINS=http://localhost:5173
|
||||||
# TLS Certificate Configuration for Backtesting Service (mTLS)
|
# TLS Certificate Configuration for Backtesting Service (mTLS)
|
||||||
# Using dev CA-signed certificates (matching Backtesting Service CA)
|
# Using dev CA-signed certificates (matching Backtesting Service CA)
|
||||||
- BACKTESTING_TLS_CA_CERT=/tmp/foxhunt/certs/ca/ca-cert.pem
|
- BACKTESTING_TLS_CA_CERT=/tmp/foxhunt/certs/ca/ca-cert.pem
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ data:
|
|||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
grpc_pass grpc://api-gateway.foxhunt.svc.cluster.local:50051;
|
grpc_pass grpc://api.foxhunt.svc.cluster.local:50051;
|
||||||
grpc_read_timeout 3600s;
|
grpc_read_timeout 3600s;
|
||||||
grpc_send_timeout 3600s;
|
grpc_send_timeout 3600s;
|
||||||
grpc_connect_timeout 30s;
|
grpc_connect_timeout 30s;
|
||||||
@@ -271,7 +271,7 @@ data:
|
|||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
grpc_pass grpc://api-gateway.foxhunt.svc.cluster.local:50051;
|
grpc_pass grpc://api.foxhunt.svc.cluster.local:50051;
|
||||||
grpc_read_timeout 3600s;
|
grpc_read_timeout 3600s;
|
||||||
grpc_send_timeout 3600s;
|
grpc_send_timeout 3600s;
|
||||||
grpc_connect_timeout 30s;
|
grpc_connect_timeout 30s;
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: S3_ENDPOINT
|
- name: S3_ENDPOINT
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: QUESTDB_ILP_HOST
|
- name: QUESTDB_ILP_HOST
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
- name: scw-registry
|
- name: scw-registry
|
||||||
containers:
|
containers:
|
||||||
- name: migrate
|
- name: migrate
|
||||||
image: rg.fr-par.scw.cloud/foxhunt/api-gateway:latest
|
image: rg.fr-par.scw.cloud/foxhunt/api:latest
|
||||||
command: ["sqlx", "migrate", "run"]
|
command: ["sqlx", "migrate", "run"]
|
||||||
env:
|
env:
|
||||||
- name: DB_PASSWORD
|
- name: DB_PASSWORD
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: NetworkPolicy
|
kind: NetworkPolicy
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api
|
||||||
namespace: foxhunt
|
namespace: foxhunt
|
||||||
spec:
|
spec:
|
||||||
podSelector:
|
podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
policyTypes:
|
policyTypes:
|
||||||
- Ingress
|
- Ingress
|
||||||
- Egress
|
- Egress
|
||||||
@@ -36,14 +36,6 @@ spec:
|
|||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 6379
|
port: 6379
|
||||||
# Web gateway
|
|
||||||
- to:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 3000
|
|
||||||
# Downstream gRPC services
|
# Downstream gRPC services
|
||||||
- to:
|
- to:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
- from:
|
- from:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 50053
|
port: 50053
|
||||||
|
|||||||
@@ -14,13 +14,10 @@ spec:
|
|||||||
- from:
|
- from:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: trading-service
|
app.kubernetes.io/name: trading-service
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 50056
|
port: 50056
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
- from:
|
- from:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 50057
|
port: 50057
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
- from:
|
- from:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 50053
|
port: 50053
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
- from:
|
- from:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
ports:
|
ports:
|
||||||
- protocol: TCP
|
- protocol: TCP
|
||||||
port: 50055
|
port: 50055
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
- from:
|
- from:
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
- podSelector:
|
- podSelector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: trading-agent-service
|
app.kubernetes.io/name: trading-agent-service
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: NetworkPolicy
|
|
||||||
metadata:
|
|
||||||
name: web-gateway
|
|
||||||
namespace: foxhunt
|
|
||||||
spec:
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
policyTypes:
|
|
||||||
- Ingress
|
|
||||||
- Egress
|
|
||||||
ingress:
|
|
||||||
- from:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: api-gateway
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: tailscale-gitlab-proxy
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 3000
|
|
||||||
egress:
|
|
||||||
# api-gateway gRPC upstream
|
|
||||||
- to:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: api-gateway
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 50051
|
|
||||||
# Direct gRPC to downstream services
|
|
||||||
- to:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: backtesting-service
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 50053
|
|
||||||
- to:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: ml-training-service
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 50053
|
|
||||||
- to:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: trading-agent-service
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 50055
|
|
||||||
- to:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: broker-gateway
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 50056
|
|
||||||
# Minio (initContainer binary fetch)
|
|
||||||
- to:
|
|
||||||
- podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: minio
|
|
||||||
ports:
|
|
||||||
- protocol: TCP
|
|
||||||
port: 9000
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api
|
||||||
namespace: foxhunt
|
namespace: foxhunt
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
app.kubernetes.io/part-of: foxhunt
|
app.kubernetes.io/part-of: foxhunt
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
maxUnavailable: 1
|
maxUnavailable: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
@@ -23,10 +23,10 @@ spec:
|
|||||||
gitlab.com/prometheus_port: "9091"
|
gitlab.com/prometheus_port: "9091"
|
||||||
gitlab.com/prometheus_path: "/metrics"
|
gitlab.com/prometheus_path: "/metrics"
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
app.kubernetes.io/part-of: foxhunt
|
app.kubernetes.io/part-of: foxhunt
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: api-gateway
|
serviceAccountName: api
|
||||||
securityContext:
|
securityContext:
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
runAsUser: 1000
|
runAsUser: 1000
|
||||||
@@ -46,16 +46,16 @@ spec:
|
|||||||
- |
|
- |
|
||||||
set -e
|
set -e
|
||||||
rclone copyto \
|
rclone copyto \
|
||||||
":s3:foxhunt-binaries/services/api-gateway" \
|
":s3:foxhunt-binaries/services/api" \
|
||||||
"/binaries/api-gateway" \
|
"/binaries/api" \
|
||||||
--s3-provider=Minio \
|
--s3-provider=Minio \
|
||||||
--s3-endpoint=https://minio.foxhunt.svc.cluster.local:9000 \
|
--s3-endpoint=https://minio.foxhunt.svc.cluster.local:9000 \
|
||||||
--s3-access-key-id="${MINIO_ACCESS_KEY}" \
|
--s3-access-key-id="${MINIO_ACCESS_KEY}" \
|
||||||
--s3-secret-access-key="${MINIO_SECRET_KEY}" \
|
--s3-secret-access-key="${MINIO_SECRET_KEY}" \
|
||||||
--s3-no-check-bucket \
|
--s3-no-check-bucket \
|
||||||
--no-check-certificate
|
--no-check-certificate
|
||||||
chmod +x /binaries/api-gateway
|
chmod +x /binaries/api
|
||||||
echo "Fetched api-gateway ($(stat -c%s /binaries/api-gateway) bytes)"
|
echo "Fetched api ($(stat -c%s /binaries/api) bytes)"
|
||||||
env:
|
env:
|
||||||
- name: MINIO_ACCESS_KEY
|
- name: MINIO_ACCESS_KEY
|
||||||
valueFrom:
|
valueFrom:
|
||||||
@@ -78,14 +78,14 @@ spec:
|
|||||||
cpu: 500m
|
cpu: 500m
|
||||||
memory: 128Mi
|
memory: 128Mi
|
||||||
containers:
|
containers:
|
||||||
- name: api-gateway
|
- name: api
|
||||||
image: rg.fr-par.scw.cloud/foxhunt-ci/foxhunt-runtime:latest
|
image: rg.fr-par.scw.cloud/foxhunt-ci/foxhunt-runtime:latest
|
||||||
securityContext:
|
securityContext:
|
||||||
allowPrivilegeEscalation: false
|
allowPrivilegeEscalation: false
|
||||||
readOnlyRootFilesystem: true
|
readOnlyRootFilesystem: true
|
||||||
capabilities:
|
capabilities:
|
||||||
drop: ["ALL"]
|
drop: ["ALL"]
|
||||||
command: ["/binaries/api-gateway"]
|
command: ["/binaries/api"]
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 50051
|
- containerPort: 50051
|
||||||
name: grpc
|
name: grpc
|
||||||
@@ -107,7 +107,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: TRADING_SERVICE_URL
|
- name: TRADING_SERVICE_URL
|
||||||
@@ -130,6 +130,8 @@ spec:
|
|||||||
value: "info,opentelemetry=warn,h2=warn,tonic=warn,hyper=warn"
|
value: "info,opentelemetry=warn,h2=warn,tonic=warn,hyper=warn"
|
||||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||||
value: "http://tempo.foxhunt.svc.cluster.local:4317"
|
value: "http://tempo.foxhunt.svc.cluster.local:4317"
|
||||||
|
- name: CORS_ORIGINS
|
||||||
|
value: "https://dashboard.fxhnt.ai,http://localhost:5173"
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: binaries
|
- name: binaries
|
||||||
mountPath: /binaries
|
mountPath: /binaries
|
||||||
@@ -169,14 +171,14 @@ spec:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api
|
||||||
namespace: foxhunt
|
namespace: foxhunt
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
app.kubernetes.io/part-of: foxhunt
|
app.kubernetes.io/part-of: foxhunt
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
ports:
|
ports:
|
||||||
- port: 50051
|
- port: 50051
|
||||||
targetPort: 50051
|
targetPort: 50051
|
||||||
@@ -188,16 +190,16 @@ spec:
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway
|
name: api
|
||||||
namespace: foxhunt
|
namespace: foxhunt
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: api-gateway
|
app.kubernetes.io/name: api
|
||||||
app.kubernetes.io/part-of: foxhunt
|
app.kubernetes.io/part-of: foxhunt
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: Role
|
kind: Role
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway-pod-reader
|
name: api-pod-reader
|
||||||
namespace: foxhunt
|
namespace: foxhunt
|
||||||
rules:
|
rules:
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
@@ -210,13 +212,13 @@ rules:
|
|||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: api-gateway-pod-reader
|
name: api-pod-reader
|
||||||
namespace: foxhunt
|
namespace: foxhunt
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: Role
|
kind: Role
|
||||||
name: api-gateway-pod-reader
|
name: api-pod-reader
|
||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: api-gateway
|
name: api
|
||||||
namespace: foxhunt
|
namespace: foxhunt
|
||||||
@@ -106,7 +106,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: BENZINGA_API_KEY
|
- name: BENZINGA_API_KEY
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: IBKR_HOST
|
- name: IBKR_HOST
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: S3_ENDPOINT
|
- name: S3_ENDPOINT
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: RUST_LOG
|
- name: RUST_LOG
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ spec:
|
|||||||
name: jwt-secret
|
name: jwt-secret
|
||||||
key: secret
|
key: secret
|
||||||
- name: JWT_ISSUER
|
- name: JWT_ISSUER
|
||||||
value: foxhunt-api-gateway
|
value: foxhunt-api
|
||||||
- name: JWT_AUDIENCE
|
- name: JWT_AUDIENCE
|
||||||
value: foxhunt-services
|
value: foxhunt-services
|
||||||
- name: QUESTDB_ILP_HOST
|
- name: QUESTDB_ILP_HOST
|
||||||
|
|||||||
@@ -1,161 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: web-gateway
|
|
||||||
namespace: foxhunt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
app.kubernetes.io/part-of: foxhunt
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
strategy:
|
|
||||||
type: RollingUpdate
|
|
||||||
rollingUpdate:
|
|
||||||
maxSurge: 0
|
|
||||||
maxUnavailable: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
annotations:
|
|
||||||
gitlab.com/prometheus_scrape: "true"
|
|
||||||
gitlab.com/prometheus_port: "9098"
|
|
||||||
gitlab.com/prometheus_path: "/metrics"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
app.kubernetes.io/part-of: foxhunt
|
|
||||||
spec:
|
|
||||||
securityContext:
|
|
||||||
runAsNonRoot: true
|
|
||||||
runAsUser: 1000
|
|
||||||
runAsGroup: 1000
|
|
||||||
fsGroup: 1000
|
|
||||||
seccompProfile:
|
|
||||||
type: RuntimeDefault
|
|
||||||
imagePullSecrets:
|
|
||||||
- name: scw-registry
|
|
||||||
nodeSelector:
|
|
||||||
k8s.scaleway.com/pool-name: foxhunt
|
|
||||||
initContainers:
|
|
||||||
- name: fetch-binary
|
|
||||||
image: rg.fr-par.scw.cloud/foxhunt-ci/foxhunt-runtime:latest
|
|
||||||
command: ["/bin/sh", "-c"]
|
|
||||||
args:
|
|
||||||
- |
|
|
||||||
set -e
|
|
||||||
RCLONE_FLAGS="--s3-provider=Minio --s3-endpoint=https://minio.foxhunt.svc.cluster.local:9000 --s3-access-key-id=${MINIO_ACCESS_KEY} --s3-secret-access-key=${MINIO_SECRET_KEY} --s3-no-check-bucket --no-check-certificate"
|
|
||||||
rclone copyto ":s3:foxhunt-binaries/services/web-gateway" "/binaries/web-gateway" $RCLONE_FLAGS
|
|
||||||
chmod +x /binaries/web-gateway
|
|
||||||
echo "Fetched web-gateway ($(stat -c%s /binaries/web-gateway) bytes)"
|
|
||||||
rclone copy ":s3:foxhunt-binaries/static/" "/binaries/static/" $RCLONE_FLAGS
|
|
||||||
echo "Fetched static assets"
|
|
||||||
env:
|
|
||||||
- name: MINIO_ACCESS_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: minio-credentials
|
|
||||||
key: access-key
|
|
||||||
- name: MINIO_SECRET_KEY
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: minio-credentials
|
|
||||||
key: secret-key
|
|
||||||
volumeMounts:
|
|
||||||
- name: binaries
|
|
||||||
mountPath: /binaries
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 64Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 256Mi
|
|
||||||
containers:
|
|
||||||
- name: web-gateway
|
|
||||||
image: rg.fr-par.scw.cloud/foxhunt-ci/foxhunt-runtime:latest
|
|
||||||
securityContext:
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
readOnlyRootFilesystem: true
|
|
||||||
capabilities:
|
|
||||||
drop: ["ALL"]
|
|
||||||
command: ["/binaries/web-gateway"]
|
|
||||||
ports:
|
|
||||||
- containerPort: 3000
|
|
||||||
name: http
|
|
||||||
- containerPort: 9098
|
|
||||||
name: metrics
|
|
||||||
env:
|
|
||||||
- name: JWT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: jwt-secret
|
|
||||||
key: secret
|
|
||||||
- name: TRADING_SERVICE_URL
|
|
||||||
value: "http://api-gateway:50051"
|
|
||||||
- name: BACKTESTING_SERVICE_URL
|
|
||||||
value: "http://backtesting-service:50053"
|
|
||||||
- name: ML_TRAINING_SERVICE_URL
|
|
||||||
value: "http://ml-training-service:50053"
|
|
||||||
- name: TRADING_AGENT_SERVICE_URL
|
|
||||||
value: "http://trading-agent-service:50055"
|
|
||||||
- name: BROKER_GATEWAY_SERVICE_URL
|
|
||||||
value: "http://broker-gateway:50056"
|
|
||||||
- name: API_GATEWAY_URL
|
|
||||||
value: "http://api-gateway:50051"
|
|
||||||
- name: RUST_LOG
|
|
||||||
value: "info,opentelemetry=warn,h2=warn,tonic=warn,hyper=warn"
|
|
||||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
|
||||||
value: "http://tempo.foxhunt.svc.cluster.local:4317"
|
|
||||||
volumeMounts:
|
|
||||||
- name: binaries
|
|
||||||
mountPath: /binaries
|
|
||||||
readOnly: true
|
|
||||||
- name: tmp
|
|
||||||
mountPath: /tmp
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 3000
|
|
||||||
initialDelaySeconds: 10
|
|
||||||
periodSeconds: 10
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /health
|
|
||||||
port: 3000
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
periodSeconds: 15
|
|
||||||
failureThreshold: 5
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 128Mi
|
|
||||||
limits:
|
|
||||||
cpu: 250m
|
|
||||||
memory: 256Mi
|
|
||||||
volumes:
|
|
||||||
- name: binaries
|
|
||||||
emptyDir:
|
|
||||||
sizeLimit: 500Mi
|
|
||||||
- name: tmp
|
|
||||||
emptyDir:
|
|
||||||
sizeLimit: 50Mi
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: web-gateway
|
|
||||||
namespace: foxhunt
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
app.kubernetes.io/part-of: foxhunt
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app.kubernetes.io/name: web-gateway
|
|
||||||
ports:
|
|
||||||
- port: 3000
|
|
||||||
targetPort: 3000
|
|
||||||
name: http
|
|
||||||
- port: 9098
|
|
||||||
targetPort: 9098
|
|
||||||
name: metrics
|
|
||||||
@@ -23,7 +23,7 @@ TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
|
|||||||
|
|
||||||
# Services built with Dockerfile.service (--build-arg SERVICE=<name>)
|
# Services built with Dockerfile.service (--build-arg SERVICE=<name>)
|
||||||
STANDARD_SERVICES=(
|
STANDARD_SERVICES=(
|
||||||
api-gateway
|
api
|
||||||
trading-service
|
trading-service
|
||||||
trading-agent-service
|
trading-agent-service
|
||||||
ml-training-service
|
ml-training-service
|
||||||
@@ -37,7 +37,6 @@ DRY_RUN=false
|
|||||||
PARALLEL=1
|
PARALLEL=1
|
||||||
SELECTED_SERVICES=()
|
SELECTED_SERVICES=()
|
||||||
SKIP_STANDARD=false
|
SKIP_STANDARD=false
|
||||||
SKIP_WEBGW=false
|
|
||||||
SKIP_TRAINING=false
|
SKIP_TRAINING=false
|
||||||
TAG_LATEST=true
|
TAG_LATEST=true
|
||||||
|
|
||||||
@@ -49,7 +48,6 @@ Options:
|
|||||||
--no-push Build only, don't push to registry
|
--no-push Build only, don't push to registry
|
||||||
--dry-run Show what would be built
|
--dry-run Show what would be built
|
||||||
--services "s1 s2" Build only these standard services
|
--services "s1 s2" Build only these standard services
|
||||||
--skip-webgw Skip web-gateway build
|
|
||||||
--skip-training Skip training image build
|
--skip-training Skip training image build
|
||||||
--parallel N Run N builds concurrently (default: 1)
|
--parallel N Run N builds concurrently (default: 1)
|
||||||
--no-latest Don't tag images as :latest
|
--no-latest Don't tag images as :latest
|
||||||
@@ -62,7 +60,6 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--no-push) DO_PUSH=false; shift ;;
|
--no-push) DO_PUSH=false; shift ;;
|
||||||
--dry-run) DRY_RUN=true; shift ;;
|
--dry-run) DRY_RUN=true; shift ;;
|
||||||
--services) IFS=' ' read -ra SELECTED_SERVICES <<< "$2"; shift 2 ;;
|
--services) IFS=' ' read -ra SELECTED_SERVICES <<< "$2"; shift 2 ;;
|
||||||
--skip-webgw) SKIP_WEBGW=true; shift ;;
|
|
||||||
--skip-training) SKIP_TRAINING=true; shift ;;
|
--skip-training) SKIP_TRAINING=true; shift ;;
|
||||||
--parallel) PARALLEL="$2"; shift 2 ;;
|
--parallel) PARALLEL="$2"; shift 2 ;;
|
||||||
--no-latest) TAG_LATEST=false; shift ;;
|
--no-latest) TAG_LATEST=false; shift ;;
|
||||||
@@ -74,7 +71,6 @@ done
|
|||||||
# If --services was given, use only those; otherwise use all standard services
|
# If --services was given, use only those; otherwise use all standard services
|
||||||
if [[ ${#SELECTED_SERVICES[@]} -gt 0 ]]; then
|
if [[ ${#SELECTED_SERVICES[@]} -gt 0 ]]; then
|
||||||
SERVICES=("${SELECTED_SERVICES[@]}")
|
SERVICES=("${SELECTED_SERVICES[@]}")
|
||||||
SKIP_WEBGW=true
|
|
||||||
SKIP_TRAINING=true
|
SKIP_TRAINING=true
|
||||||
else
|
else
|
||||||
SERVICES=("${STANDARD_SERVICES[@]}")
|
SERVICES=("${STANDARD_SERVICES[@]}")
|
||||||
@@ -195,16 +191,6 @@ else
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# Build web-gateway (Dockerfile.web-gateway)
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
if [[ "$SKIP_WEBGW" != "true" ]]; then
|
|
||||||
echo ""
|
|
||||||
echo "--- Web Gateway ---"
|
|
||||||
build_and_tag "web-gateway" "infra/docker/Dockerfile.web-gateway" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Build training image (Dockerfile.training)
|
# Build training image (Dockerfile.training)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -227,10 +213,6 @@ if [[ "$DO_PUSH" == "true" ]]; then
|
|||||||
push_image "$svc"
|
push_image "$svc"
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "$SKIP_WEBGW" != "true" ]]; then
|
|
||||||
push_image "web-gateway"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$SKIP_TRAINING" != "true" ]]; then
|
if [[ "$SKIP_TRAINING" != "true" ]]; then
|
||||||
push_image "training"
|
push_image "training"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -87,23 +87,23 @@ check "QuestDB HTTP health" \
|
|||||||
"
|
"
|
||||||
|
|
||||||
# --- 4. Services ----------------------------------------------------------
|
# --- 4. Services ----------------------------------------------------------
|
||||||
section "Services (gRPC + web-gateway)"
|
section "Services (gRPC + API gateway)"
|
||||||
|
|
||||||
for svc in "${GRPC_SERVICES[@]}"; do
|
for svc in "${GRPC_SERVICES[@]}"; do
|
||||||
check "Pod '${svc}' is Running" \
|
check "Pod '${svc}' is Running" \
|
||||||
bash -c "kubectl -n ${NAMESPACE} get pods -l app=${svc} --field-selector=status.phase=Running --no-headers 2>/dev/null | grep -q ."
|
bash -c "kubectl -n ${NAMESPACE} get pods -l app=${svc} --field-selector=status.phase=Running --no-headers 2>/dev/null | grep -q ."
|
||||||
done
|
done
|
||||||
|
|
||||||
check "Pod 'web-gateway' is Running" \
|
check "Pod 'api' is Running" \
|
||||||
bash -c "kubectl -n ${NAMESPACE} get pods -l app=web-gateway --field-selector=status.phase=Running --no-headers 2>/dev/null | grep -q ."
|
bash -c "kubectl -n ${NAMESPACE} get pods -l app=api --field-selector=status.phase=Running --no-headers 2>/dev/null | grep -q ."
|
||||||
|
|
||||||
# --- 5. Web gateway health ------------------------------------------------
|
# --- 5. API gateway health ------------------------------------------------
|
||||||
section "Web Gateway Health"
|
section "API Gateway Health"
|
||||||
|
|
||||||
check "web-gateway /health returns 200" \
|
check "api /health returns 200" \
|
||||||
bash -c "
|
bash -c "
|
||||||
CLUSTER_IP=\$(kubectl -n ${NAMESPACE} get svc web-gateway -o jsonpath='{.spec.clusterIP}' 2>/dev/null)
|
CLUSTER_IP=\$(kubectl -n ${NAMESPACE} get svc api -o jsonpath='{.spec.clusterIP}' 2>/dev/null)
|
||||||
PORT=\$(kubectl -n ${NAMESPACE} get svc web-gateway -o jsonpath='{.spec.ports[0].port}' 2>/dev/null)
|
PORT=\$(kubectl -n ${NAMESPACE} get svc api -o jsonpath='{.spec.ports[0].port}' 2>/dev/null)
|
||||||
kubectl -n ${NAMESPACE} run smoke-curl --rm -i --restart=Never --image=curlimages/curl -- \
|
kubectl -n ${NAMESPACE} run smoke-curl --rm -i --restart=Never --image=curlimages/curl -- \
|
||||||
curl -sf http://\${CLUSTER_IP}:\${PORT}/health
|
curl -sf http://\${CLUSTER_IP}:\${PORT}/health
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ PRODUCTION_MODULES=(
|
|||||||
"config"
|
"config"
|
||||||
"common"
|
"common"
|
||||||
"services/trading_service"
|
"services/trading_service"
|
||||||
"services/api_gateway"
|
"services/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Print colored message
|
# Print colored message
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ DNS.1 = localhost
|
|||||||
DNS.2 = trading.foxhunt.local
|
DNS.2 = trading.foxhunt.local
|
||||||
DNS.3 = backtesting.foxhunt.local
|
DNS.3 = backtesting.foxhunt.local
|
||||||
DNS.4 = ml-training.foxhunt.local
|
DNS.4 = ml-training.foxhunt.local
|
||||||
DNS.5 = api-gateway.foxhunt.local
|
DNS.5 = api.foxhunt.local
|
||||||
IP.1 = 127.0.0.1
|
IP.1 = 127.0.0.1
|
||||||
IP.2 = 0.0.0.0
|
IP.2 = 0.0.0.0
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
Reference in New Issue
Block a user