From 6b35b04d20a860826a7fb7a4547d250b222d14b7 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 06:11:26 -0700 Subject: [PATCH] feat: load service URLs from ConfigMap, not hardcoded --- crates/mem-cli/src/config.rs | 93 ++++++++++++++++++++++++++ crates/mem-cli/src/lib.rs | 1 + crates/mem-cli/src/main.rs | 4 +- k8s/app/memory-deployment.yaml | 95 +++++++++++++++++++++++++++ k8s/app/memory-service-configmap.yaml | 19 ++++++ 5 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 crates/mem-cli/src/config.rs create mode 100644 k8s/app/memory-deployment.yaml create mode 100644 k8s/app/memory-service-configmap.yaml diff --git a/crates/mem-cli/src/config.rs b/crates/mem-cli/src/config.rs new file mode 100644 index 0000000..66f4e0d --- /dev/null +++ b/crates/mem-cli/src/config.rs @@ -0,0 +1,93 @@ +/// Configuration management for inter-pod URLs via environment variables (ConfigMap) +/// All service URLs come from K8s ConfigMap, never hardcoded +/// +/// ConfigMap in K8s: +/// ```yaml +/// apiVersion: v1 +/// kind: ConfigMap +/// metadata: +/// name: memory-service-config +/// namespace: poimen +/// data: +/// MEMORY_SERVICE_ADDR: "http://memory-service.poimen.svc.cluster.local:8080" +/// AUTHENTIK_ISSUER: "http://authentik.iam.svc.cluster.local/application/o/poimen-memory/" +/// WEBHOOK_URL: "http://temporal-webhook.temporal.svc.cluster.local:9000/webhook" +/// ``` + +use anyhow::{anyhow, Result}; + +#[derive(Debug, Clone)] +pub struct ServiceConfig { + /// Memory service address (this service itself) + pub memory_service_addr: String, + + /// Authentik OIDC issuer endpoint + pub authentik_issuer: String, + + /// Temporal webhook callback URL + pub webhook_url: String, + + /// OpenSearch cluster endpoint + pub opensearch_url: String, + + /// PostgreSQL connection string + pub database_url: String, +} + +impl ServiceConfig { + /// Load configuration from environment variables (set by K8s ConfigMap) + /// Fails if required env vars are missing + pub fn from_env() -> Result { + let memory_service_addr = std::env::var("MEMORY_SERVICE_ADDR") + .unwrap_or_else(|_| "http://localhost:8080".to_string()); + + let authentik_issuer = std::env::var("AUTHENTIK_ISSUER") + .map_err(|_| anyhow!("AUTHENTIK_ISSUER env var not set (configure in ConfigMap)"))?; + + let webhook_url = std::env::var("WEBHOOK_URL") + .map_err(|_| anyhow!("WEBHOOK_URL env var not set (configure in ConfigMap)"))?; + + let opensearch_url = std::env::var("OPENSEARCH_URL") + .map_err(|_| anyhow!("OPENSEARCH_URL env var not set (configure in ConfigMap)"))?; + + let database_url = std::env::var("DATABASE_URL") + .map_err(|_| anyhow!("DATABASE_URL env var not set (configure Secret or ConfigMap)"))?; + + Ok(ServiceConfig { + memory_service_addr, + authentik_issuer, + webhook_url, + opensearch_url, + database_url, + }) + } + + /// Load with defaults for development (localhost only) + pub fn from_env_dev() -> Self { + ServiceConfig { + memory_service_addr: std::env::var("MEMORY_SERVICE_ADDR") + .unwrap_or_else(|_| "http://localhost:8080".to_string()), + authentik_issuer: std::env::var("AUTHENTIK_ISSUER") + .unwrap_or_else(|_| "http://localhost:8080/application/o/poimen-memory/".to_string()), + webhook_url: std::env::var("WEBHOOK_URL") + .unwrap_or_else(|_| "http://localhost:9000/webhook".to_string()), + opensearch_url: std::env::var("OPENSEARCH_URL") + .unwrap_or_else(|_| "http://localhost:9200".to_string()), + database_url: std::env::var("DATABASE_URL") + .unwrap_or_else(|_| "postgres://localhost/memory".to_string()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_config_from_env_dev() { + let config = ServiceConfig::from_env_dev(); + assert_eq!(config.memory_service_addr, "http://localhost:8080"); + assert!(config.authentik_issuer.contains("localhost")); + assert!(config.webhook_url.contains("localhost")); + } +} diff --git a/crates/mem-cli/src/lib.rs b/crates/mem-cli/src/lib.rs index 8701b89..26f6904 100644 --- a/crates/mem-cli/src/lib.rs +++ b/crates/mem-cli/src/lib.rs @@ -1,3 +1,4 @@ +pub mod config; pub mod endpoints; pub mod handlers; pub mod http_server; diff --git a/crates/mem-cli/src/main.rs b/crates/mem-cli/src/main.rs index 64e61fd..7601d59 100644 --- a/crates/mem-cli/src/main.rs +++ b/crates/mem-cli/src/main.rs @@ -448,8 +448,10 @@ async fn cmd_learn( all_files.sort(); println!("Found {} markdown files", all_files.len()); + // Load configuration from environment (set by K8s ConfigMap) + let config = mem_cli::config::ServiceConfig::from_env_dev(); let api_url = std::env::var("MEM_API_URL") - .unwrap_or_else(|_| "http://localhost:8080".to_string()); + .unwrap_or_else(|_| config.memory_service_addr.clone()); let api_token = std::env::var("MEM_API_TOKEN").ok(); let http = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(120)) diff --git a/k8s/app/memory-deployment.yaml b/k8s/app/memory-deployment.yaml new file mode 100644 index 0000000..620e016 --- /dev/null +++ b/k8s/app/memory-deployment.yaml @@ -0,0 +1,95 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: memory-service + namespace: poimen + labels: + app: memory-service +spec: + replicas: 2 + selector: + matchLabels: + app: memory-service + template: + metadata: + labels: + app: memory-service + spec: + serviceAccountName: memory-service + containers: + - name: memory-service + image: forgejo.riotpiao.com/rock/poimen-memory:latest + imagePullPolicy: Always + ports: + - name: http + containerPort: 8080 + protocol: TCP + + # Read all config from ConfigMap + envFrom: + - configMapRef: + name: memory-service-config + + # Secrets (override/add to ConfigMap) + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: memory-db-credentials + key: connection-string + - name: MEM_API_KEY + valueFrom: + secretKeyRef: + name: memory-api-keys + key: primary-key + + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: 1000m + memory: 1Gi + + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + + # ImagePullSecret for private registry + imagePullSecrets: + - name: registry-credentials + +--- +apiVersion: v1 +kind: Service +metadata: + name: memory-service + namespace: poimen + labels: + app: memory-service +spec: + type: ClusterIP + ports: + - name: http + port: 8080 + targetPort: http + protocol: TCP + selector: + app: memory-service + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: memory-service + namespace: poimen diff --git a/k8s/app/memory-service-configmap.yaml b/k8s/app/memory-service-configmap.yaml new file mode 100644 index 0000000..df2a0aa --- /dev/null +++ b/k8s/app/memory-service-configmap.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: memory-service-config + namespace: poimen + labels: + app: memory-service + version: "1" +data: + # Inter-pod URLs (cluster-internal DNS) + MEMORY_SERVICE_ADDR: "http://memory-service.poimen.svc.cluster.local:8080" + AUTHENTIK_ISSUER: "http://authentik-server.iam.svc.cluster.local/application/o/poimen-memory/" + WEBHOOK_URL: "http://temporal-webhook.temporal.svc.cluster.local:9000/webhook" + OPENSEARCH_URL: "http://opensearch.poimen.svc.cluster.local:9200" + + # Service addresses for internal communication + POSTGRES_HOST: "memory-db.poimen.svc.cluster.local" + POSTGRES_PORT: "5432" + POSTGRES_DB: "memory"