trading-bot: revive K8s stack + add meet-kevin-watcher
Uncomment the trading-bot stack (disabled 2026-04-06 due to resource consumption) and add the new meet_kevin_watcher service container. Changes: - Uncomment the /* ... */ block enclosing the entire stack - Fix db_init job: add -d postgres to psql commands (root user has no root-named database — matches pattern used in claude-memory + others) - Remove 3 disabled containers from trading-bot-workers Pod spec: news-fetcher, sentiment-analyzer, trade-executor - Add new meet-kevin-watcher container (image viktorbarzin/trading-bot-service:latest, command python -m services.meet_kevin_watcher.main, mem 128Mi/256Mi) - Extend ExternalSecret with TRADING_OPENROUTER_API_KEY and TRADING_MEET_KEVIN_CHANNEL_ID keys (sourced from Vault secret/trading-bot) - Add 4 common_env entries for the Meet Kevin pipeline (poll interval, daily cost cap, model slug, prompt version) - Update lifecycle.ignore_changes to 4 image indices vault: re-enable pg-trading static role - Add pg-trading to vault_database_secret_backend_connection allowed_roles - Uncomment vault_database_secret_backend_static_role.pg_trading (was disabled 2026-04-06 with the rest of trading-bot stack) kyverno: add postgres* to trusted-registries allowlist - trading-bot db_init uses postgres:16-alpine (Docker Hub library image) - postgres* was not in the DockerHub bare-name allowlist (unlike mysql*, alpine*, nginx*, python* which were already there) Final workers Pod containers (in order): [0] signal-generator [1] learning-engine [2] market-data [3] meet-kevin-watcher (NEW) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d0a4876825
commit
854817e2e3
5 changed files with 84 additions and 145 deletions
|
|
@ -329,7 +329,7 @@ resource "kubectl_manifest" "policy_require_trusted_registries" {
|
||||||
# Private
|
# Private
|
||||||
"forgejo.viktorbarzin.me/*", "10.0.20.10*",
|
"forgejo.viktorbarzin.me/*", "10.0.20.10*",
|
||||||
# DockerHub library (bare image names without slash)
|
# DockerHub library (bare image names without slash)
|
||||||
"alpine*", "busybox*", "kong*", "mysql*", "nginx*", "python*",
|
"alpine*", "busybox*", "kong*", "mysql*", "nginx*", "postgres*", "python*",
|
||||||
# DockerHub user repos (no registry prefix, has slash) —
|
# DockerHub user repos (no registry prefix, has slash) —
|
||||||
# enumerated from current cluster state.
|
# enumerated from current cluster state.
|
||||||
"actualbudget/*", "afadil/*", "binwiederhier/*", "bitnami/*",
|
"actualbudget/*", "afadil/*", "binwiederhier/*", "bitnami/*",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa
|
# Generated by Terragrunt. Sig: nIlQXj57tbuaRZEa
|
||||||
terraform {
|
terraform {
|
||||||
backend "pg" {
|
backend "pg" {
|
||||||
conn_str = "postgres://terraform_state:ZCcWMOLCTqb0aV-XyTAZ@10.0.20.200:5432/terraform_state?sslmode=disable"
|
conn_str = "postgres://terraform_state:LicuZK1nVl4ILE5HF-A9@10.0.20.200:5432/terraform_state?sslmode=disable"
|
||||||
schema_name = "trading-bot"
|
schema_name = "trading-bot"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
/*
|
|
||||||
# TRADING-BOT STACK COMMENTED OUT - 2026-04-06
|
|
||||||
# Deployments scaled to 0, infrastructure disabled to prevent re-creation on apply
|
|
||||||
# To re-enable: uncomment this entire block
|
|
||||||
|
|
||||||
variable "tls_secret_name" {
|
variable "tls_secret_name" {
|
||||||
type = string
|
type = string
|
||||||
sensitive = true
|
sensitive = true
|
||||||
|
|
@ -27,6 +22,10 @@ locals {
|
||||||
TRADING_RP_NAME = "Trading Bot"
|
TRADING_RP_NAME = "Trading Bot"
|
||||||
TRADING_RP_ORIGIN = "https://trading.viktorbarzin.me"
|
TRADING_RP_ORIGIN = "https://trading.viktorbarzin.me"
|
||||||
TRADING_CORS_ORIGINS = "[\"https://trading.viktorbarzin.me\"]"
|
TRADING_CORS_ORIGINS = "[\"https://trading.viktorbarzin.me\"]"
|
||||||
|
TRADING_MEET_KEVIN_POLL_INTERVAL_SECONDS = "10800"
|
||||||
|
TRADING_MEET_KEVIN_DAILY_COST_CAP_USD = "5"
|
||||||
|
TRADING_MEET_KEVIN_LLM_MODEL = "anthropic/claude-sonnet-4.5"
|
||||||
|
TRADING_MEET_KEVIN_PROMPT_VERSION = "v1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,6 +71,8 @@ resource "kubernetes_manifest" "external_secret" {
|
||||||
TRADING_ALPHA_VANTAGE_API_KEY = "{{ .alpha_vantage_api_key }}"
|
TRADING_ALPHA_VANTAGE_API_KEY = "{{ .alpha_vantage_api_key }}"
|
||||||
TRADING_FMP_API_KEY = "{{ .fmp_api_key }}"
|
TRADING_FMP_API_KEY = "{{ .fmp_api_key }}"
|
||||||
DBAAS_ROOT_PASSWORD = "{{ .dbaas_root_password }}"
|
DBAAS_ROOT_PASSWORD = "{{ .dbaas_root_password }}"
|
||||||
|
TRADING_OPENROUTER_API_KEY = "{{ .openrouter_api_key }}"
|
||||||
|
TRADING_MEET_KEVIN_CHANNEL_ID = "{{ .meet_kevin_channel_id }}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +85,8 @@ resource "kubernetes_manifest" "external_secret" {
|
||||||
{ secretKey = "alpha_vantage_api_key", remoteRef = { key = "trading-bot", property = "alpha_vantage_api_key" } },
|
{ secretKey = "alpha_vantage_api_key", remoteRef = { key = "trading-bot", property = "alpha_vantage_api_key" } },
|
||||||
{ secretKey = "fmp_api_key", remoteRef = { key = "trading-bot", property = "fmp_api_key" } },
|
{ secretKey = "fmp_api_key", remoteRef = { key = "trading-bot", property = "fmp_api_key" } },
|
||||||
{ secretKey = "dbaas_root_password", remoteRef = { key = "trading-bot", property = "dbaas_root_password" } },
|
{ secretKey = "dbaas_root_password", remoteRef = { key = "trading-bot", property = "dbaas_root_password" } },
|
||||||
|
{ secretKey = "openrouter_api_key", remoteRef = { key = "trading-bot", property = "openrouter_api_key" } },
|
||||||
|
{ secretKey = "meet_kevin_channel_id", remoteRef = { key = "trading-bot", property = "meet_kevin_channel_id" } },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,14 +146,16 @@ resource "kubernetes_job" "db_init" {
|
||||||
"sh", "-c",
|
"sh", "-c",
|
||||||
<<-EOT
|
<<-EOT
|
||||||
set -e
|
set -e
|
||||||
|
# -d postgres: psql defaults database name to username; root user
|
||||||
|
# doesn't have a root-named database, so be explicit.
|
||||||
# Create role if not exists
|
# Create role if not exists
|
||||||
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_roles WHERE rolname='trading'" | grep -q 1 || \
|
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d postgres -tc "SELECT 1 FROM pg_roles WHERE rolname='trading'" | grep -q 1 || \
|
||||||
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -c "CREATE ROLE trading WITH LOGIN PASSWORD '$DB_PASSWORD'"
|
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d postgres -c "CREATE ROLE trading WITH LOGIN PASSWORD '$DB_PASSWORD'"
|
||||||
# Create database if not exists
|
# Create database if not exists
|
||||||
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -tc "SELECT 1 FROM pg_database WHERE datname='trading'" | grep -q 1 || \
|
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d postgres -tc "SELECT 1 FROM pg_database WHERE datname='trading'" | grep -q 1 || \
|
||||||
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -c "CREATE DATABASE trading OWNER trading"
|
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d postgres -c "CREATE DATABASE trading OWNER trading"
|
||||||
# Grant privileges
|
# Grant privileges
|
||||||
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -c "GRANT ALL PRIVILEGES ON DATABASE trading TO trading"
|
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE trading TO trading"
|
||||||
# Try to enable timescaledb (allow failure)
|
# Try to enable timescaledb (allow failure)
|
||||||
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d trading -c "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE" || true
|
PGPASSWORD="$DBAAS_ROOT_PASSWORD" psql -h ${var.postgresql_host} -U root -d trading -c "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE" || true
|
||||||
echo "Database init complete"
|
echo "Database init complete"
|
||||||
|
|
@ -358,78 +363,6 @@ resource "kubernetes_deployment" "trading-bot-workers" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spec {
|
spec {
|
||||||
container {
|
|
||||||
name = "news-fetcher"
|
|
||||||
image = "viktorbarzin/trading-bot-service:latest"
|
|
||||||
image_pull_policy = "Always"
|
|
||||||
command = ["python", "-m", "services.news_fetcher.main"]
|
|
||||||
dynamic "env" {
|
|
||||||
for_each = local.common_env
|
|
||||||
content {
|
|
||||||
name = env.key
|
|
||||||
value = env.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env {
|
|
||||||
name = "TRADING_OTEL_METRICS_PORT"
|
|
||||||
value = "9091"
|
|
||||||
}
|
|
||||||
env_from {
|
|
||||||
secret_ref {
|
|
||||||
name = "trading-bot-secrets"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env_from {
|
|
||||||
secret_ref {
|
|
||||||
name = "trading-bot-db-creds"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
requests = {
|
|
||||||
cpu = "10m"
|
|
||||||
memory = "128Mi"
|
|
||||||
}
|
|
||||||
limits = {
|
|
||||||
memory = "256Mi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
container {
|
|
||||||
name = "sentiment-analyzer"
|
|
||||||
image = "viktorbarzin/trading-bot-service:latest"
|
|
||||||
image_pull_policy = "Always"
|
|
||||||
command = ["python", "-m", "services.sentiment_analyzer.main"]
|
|
||||||
dynamic "env" {
|
|
||||||
for_each = local.common_env
|
|
||||||
content {
|
|
||||||
name = env.key
|
|
||||||
value = env.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env {
|
|
||||||
name = "TRADING_OTEL_METRICS_PORT"
|
|
||||||
value = "9092"
|
|
||||||
}
|
|
||||||
env_from {
|
|
||||||
secret_ref {
|
|
||||||
name = "trading-bot-secrets"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env_from {
|
|
||||||
secret_ref {
|
|
||||||
name = "trading-bot-db-creds"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
requests = {
|
|
||||||
cpu = "100m"
|
|
||||||
memory = "512Mi"
|
|
||||||
}
|
|
||||||
limits = {
|
|
||||||
memory = "512Mi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
container {
|
container {
|
||||||
name = "signal-generator"
|
name = "signal-generator"
|
||||||
image = "viktorbarzin/trading-bot-service:latest"
|
image = "viktorbarzin/trading-bot-service:latest"
|
||||||
|
|
@ -466,42 +399,6 @@ resource "kubernetes_deployment" "trading-bot-workers" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
container {
|
|
||||||
name = "trade-executor"
|
|
||||||
image = "viktorbarzin/trading-bot-service:latest"
|
|
||||||
image_pull_policy = "Always"
|
|
||||||
command = ["python", "-m", "services.trade_executor.main"]
|
|
||||||
dynamic "env" {
|
|
||||||
for_each = local.common_env
|
|
||||||
content {
|
|
||||||
name = env.key
|
|
||||||
value = env.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env {
|
|
||||||
name = "TRADING_OTEL_METRICS_PORT"
|
|
||||||
value = "9094"
|
|
||||||
}
|
|
||||||
env_from {
|
|
||||||
secret_ref {
|
|
||||||
name = "trading-bot-secrets"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
env_from {
|
|
||||||
secret_ref {
|
|
||||||
name = "trading-bot-db-creds"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resources {
|
|
||||||
requests = {
|
|
||||||
cpu = "10m"
|
|
||||||
memory = "128Mi"
|
|
||||||
}
|
|
||||||
limits = {
|
|
||||||
memory = "256Mi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
container {
|
container {
|
||||||
name = "learning-engine"
|
name = "learning-engine"
|
||||||
image = "viktorbarzin/trading-bot-service:latest"
|
image = "viktorbarzin/trading-bot-service:latest"
|
||||||
|
|
@ -574,18 +471,52 @@ resource "kubernetes_deployment" "trading-bot-workers" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
container {
|
||||||
|
name = "meet-kevin-watcher"
|
||||||
|
image = "viktorbarzin/trading-bot-service:latest"
|
||||||
|
image_pull_policy = "Always"
|
||||||
|
command = ["python", "-m", "services.meet_kevin_watcher.main"]
|
||||||
|
dynamic "env" {
|
||||||
|
for_each = local.common_env
|
||||||
|
content {
|
||||||
|
name = env.key
|
||||||
|
value = env.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env {
|
||||||
|
name = "TRADING_OTEL_METRICS_PORT"
|
||||||
|
value = "9097"
|
||||||
|
}
|
||||||
|
env_from {
|
||||||
|
secret_ref {
|
||||||
|
name = "trading-bot-secrets"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env_from {
|
||||||
|
secret_ref {
|
||||||
|
name = "trading-bot-db-creds"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
requests = {
|
||||||
|
cpu = "10m"
|
||||||
|
memory = "128Mi"
|
||||||
|
}
|
||||||
|
limits = {
|
||||||
|
memory = "256Mi"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycle {
|
lifecycle {
|
||||||
# DRIFT_WORKAROUND: CI pipeline owns image tags for all 6 worker containers. Reviewed 2026-04-18.
|
# DRIFT_WORKAROUND: CI pipeline owns image tags for all 4 worker containers. Reviewed 2026-05-22.
|
||||||
ignore_changes = [
|
ignore_changes = [
|
||||||
spec[0].template[0].spec[0].container[0].image,
|
spec[0].template[0].spec[0].container[0].image,
|
||||||
spec[0].template[0].spec[0].container[1].image,
|
spec[0].template[0].spec[0].container[1].image,
|
||||||
spec[0].template[0].spec[0].container[2].image,
|
spec[0].template[0].spec[0].container[2].image,
|
||||||
spec[0].template[0].spec[0].container[3].image,
|
spec[0].template[0].spec[0].container[3].image,
|
||||||
spec[0].template[0].spec[0].container[4].image,
|
|
||||||
spec[0].template[0].spec[0].container[5].image,
|
|
||||||
spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2
|
spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -628,6 +559,5 @@ module "ingress" {
|
||||||
"gethomepage.dev/pod-selector" = ""
|
"gethomepage.dev/pod-selector" = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
# CI retrigger v6 2026-05-16T23:18:58Z
|
# CI retrigger v6 2026-05-16T23:18:58Z
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,13 @@ terraform {
|
||||||
source = "goauthentik/authentik"
|
source = "goauthentik/authentik"
|
||||||
version = "~> 2024.10"
|
version = "~> 2024.10"
|
||||||
}
|
}
|
||||||
|
# kubectl (gavinbunney) — workaround for hashicorp/kubernetes
|
||||||
|
# `kubernetes_manifest` panics on Kyverno CRDs. See beads code-e2dp.
|
||||||
|
# Declared for all stacks but only used where opted-in.
|
||||||
|
kubectl = {
|
||||||
|
source = "gavinbunney/kubectl"
|
||||||
|
version = "~> 1.14"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,3 +42,8 @@ provider "vault" {
|
||||||
address = "https://vault.viktorbarzin.me"
|
address = "https://vault.viktorbarzin.me"
|
||||||
skip_child_token = true
|
skip_child_token = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provider "kubectl" {
|
||||||
|
config_path = var.kube_config_path
|
||||||
|
load_config_file = true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -610,7 +610,7 @@ resource "vault_database_secret_backend_connection" "postgresql" {
|
||||||
backend = vault_mount.database.path
|
backend = vault_mount.database.path
|
||||||
name = "postgresql"
|
name = "postgresql"
|
||||||
allowed_roles = [
|
allowed_roles = [
|
||||||
# "pg-trading", # Commented out 2026-04-06 - trading-bot disabled
|
"pg-trading",
|
||||||
"pg-health", "pg-linkwarden",
|
"pg-health", "pg-linkwarden",
|
||||||
"pg-affine", "pg-woodpecker", "pg-claude-memory",
|
"pg-affine", "pg-woodpecker", "pg-claude-memory",
|
||||||
"pg-terraform-state", "pg-payslip-ingest", "pg-job-hunter",
|
"pg-terraform-state", "pg-payslip-ingest", "pg-job-hunter",
|
||||||
|
|
@ -696,8 +696,6 @@ resource "vault_database_secret_backend_static_role" "mysql_phpipam" {
|
||||||
|
|
||||||
# --- PostgreSQL Static Roles ---
|
# --- PostgreSQL Static Roles ---
|
||||||
|
|
||||||
/*
|
|
||||||
# Commented out 2026-04-06 - trading-bot disabled
|
|
||||||
resource "vault_database_secret_backend_static_role" "pg_trading" {
|
resource "vault_database_secret_backend_static_role" "pg_trading" {
|
||||||
backend = vault_mount.database.path
|
backend = vault_mount.database.path
|
||||||
db_name = vault_database_secret_backend_connection.postgresql.name
|
db_name = vault_database_secret_backend_connection.postgresql.name
|
||||||
|
|
@ -705,7 +703,6 @@ resource "vault_database_secret_backend_static_role" "pg_trading" {
|
||||||
username = "trading"
|
username = "trading"
|
||||||
rotation_period = 604800
|
rotation_period = 604800
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
resource "vault_database_secret_backend_static_role" "pg_health" {
|
resource "vault_database_secret_backend_static_role" "pg_health" {
|
||||||
backend = vault_mount.database.path
|
backend = vault_mount.database.path
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue