feat: add Listmonk newsletter platform on Legion k3s
- Namespace, ExternalSecrets (Vault), DB init job, deployment, service, PVC, ingress - Routes newsletter.harmonic-framework.com via existing Cloudflare tunnel - Uses shared platform postgres with dedicated listmonk DB/user - Credentials stored at secret/data/listmonk in Vault
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: listmonk
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://git.neuralplatform.ai/will/infrastructure.git
|
||||
targetRevision: main
|
||||
path: servers/legion/k8s/listmonk
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: listmonk
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
# One-time job to create the listmonk database and user in the shared postgres.
|
||||
# Safe to re-run (DO $$ guards prevent duplicate creation).
|
||||
# Argo CD will re-apply if deleted; ttlSecondsAfterFinished cleans it up automatically.
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: listmonk-db-init
|
||||
namespace: listmonk
|
||||
spec:
|
||||
ttlSecondsAfterFinished: 300
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: psql
|
||||
image: postgres:16-alpine
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
PG_URI="postgresql://postgres:${POSTGRES_PASSWORD}@postgres-postgresql.platform.svc.cluster.local:5432/postgres"
|
||||
# Create user if missing
|
||||
psql "$PG_URI" -c "DO \$\$ BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${DB_USER}') THEN
|
||||
EXECUTE format('CREATE USER %I WITH PASSWORD %L', '${DB_USER}', '${DB_PASSWORD}');
|
||||
END IF;
|
||||
END \$\$;"
|
||||
# Create DB if missing
|
||||
psql "$PG_URI" -c "SELECT 'CREATE DATABASE ${DB_NAME} OWNER ${DB_USER}'" \
|
||||
| grep -q 'CREATE DATABASE' && \
|
||||
psql "$PG_URI" -c "CREATE DATABASE ${DB_NAME} OWNER ${DB_USER};" || true
|
||||
# Ensure privileges
|
||||
psql "$PG_URI" -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER};"
|
||||
echo "DB init complete."
|
||||
env:
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-superuser
|
||||
key: password
|
||||
- name: DB_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_user
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_password
|
||||
- name: DB_NAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_name
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: listmonk
|
||||
namespace: listmonk
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: listmonk
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: listmonk
|
||||
spec:
|
||||
initContainers:
|
||||
# Run `--install` on first boot to create the schema; no-op on subsequent starts
|
||||
- name: listmonk-install
|
||||
image: listmonk/listmonk:v4.1.0
|
||||
args: ["--install", "--yes", "--idempotent"]
|
||||
env:
|
||||
- name: LISTMONK_db__host
|
||||
value: "postgres-postgresql.platform.svc.cluster.local"
|
||||
- name: LISTMONK_db__port
|
||||
value: "5432"
|
||||
- name: LISTMONK_db__name
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_name
|
||||
- name: LISTMONK_db__user
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_user
|
||||
- name: LISTMONK_db__password
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_password
|
||||
- name: LISTMONK_db__ssl_mode
|
||||
value: "disable"
|
||||
- name: LISTMONK_app__admin_username
|
||||
value: "admin"
|
||||
- name: LISTMONK_app__admin_password
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: admin_password
|
||||
containers:
|
||||
- name: listmonk
|
||||
image: listmonk/listmonk:v4.1.0
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
env:
|
||||
- name: LISTMONK_db__host
|
||||
value: "postgres-postgresql.platform.svc.cluster.local"
|
||||
- name: LISTMONK_db__port
|
||||
value: "5432"
|
||||
- name: LISTMONK_db__name
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_name
|
||||
- name: LISTMONK_db__user
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_user
|
||||
- name: LISTMONK_db__password
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: db_password
|
||||
- name: LISTMONK_db__ssl_mode
|
||||
value: "disable"
|
||||
- name: LISTMONK_app__admin_username
|
||||
value: "admin"
|
||||
- name: LISTMONK_app__admin_password
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: admin_password
|
||||
- name: LISTMONK_app__secret_key
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: listmonk-secrets
|
||||
key: app_key
|
||||
volumeMounts:
|
||||
- name: uploads
|
||||
mountPath: /listmonk/uploads
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 9000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
volumes:
|
||||
- name: uploads
|
||||
persistentVolumeClaim:
|
||||
claimName: listmonk-uploads
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: listmonk
|
||||
namespace: listmonk
|
||||
spec:
|
||||
selector:
|
||||
app: listmonk
|
||||
ports:
|
||||
- port: 9000
|
||||
targetPort: 9000
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
# listmonk-secrets — DB credentials and admin password from Vault
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: listmonk-secrets
|
||||
namespace: listmonk
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: listmonk-secrets
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: db_password
|
||||
remoteRef:
|
||||
key: secret/data/listmonk
|
||||
property: db_password
|
||||
- secretKey: db_user
|
||||
remoteRef:
|
||||
key: secret/data/listmonk
|
||||
property: db_user
|
||||
- secretKey: db_name
|
||||
remoteRef:
|
||||
key: secret/data/listmonk
|
||||
property: db_name
|
||||
- secretKey: admin_password
|
||||
remoteRef:
|
||||
key: secret/data/listmonk
|
||||
property: admin_password
|
||||
- secretKey: app_key
|
||||
remoteRef:
|
||||
key: secret/data/listmonk
|
||||
property: app_key
|
||||
---
|
||||
# postgres superuser password — needed for DB init job
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: postgres-superuser
|
||||
namespace: listmonk
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: postgres-superuser
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: secret/data/legion-db
|
||||
property: postgres_password
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: listmonk
|
||||
namespace: listmonk
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
- newsletter.harmonic-framework.com
|
||||
secretName: listmonk-tls
|
||||
rules:
|
||||
- host: newsletter.harmonic-framework.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: listmonk
|
||||
port:
|
||||
number: 9000
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: listmonk-uploads
|
||||
namespace: listmonk
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
@@ -47,3 +47,13 @@ resource "kubernetes_namespace" "harmonic_wordpress" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_namespace" "listmonk" {
|
||||
metadata {
|
||||
name = "listmonk"
|
||||
labels = {
|
||||
managed-by = "terraform"
|
||||
tier = "apps"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user