nextcloud: integration with mailu & gitops-ui: initial install
This commit is contained in:
parent
0d0216c8f5
commit
8fceebd7a7
@ -1,3 +1,3 @@
|
||||
# titan-iac
|
||||
|
||||
Flux-managed Kubernetes cluster for bstein.dev services. See `AGENTS.md` for contributor guidance and service-specific manifests under `services/` and `infrastructure/`.
|
||||
Flux-managed Kubernetes cluster for bstein.dev services.
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
# clusters/atlas/flux-system/platform/gitops-ui/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: gitops-ui
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/gitops-ui
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
targetNamespace: flux-system
|
||||
dependsOn:
|
||||
- name: helm
|
||||
- name: traefik
|
||||
wait: true
|
||||
@ -5,5 +5,6 @@ resources:
|
||||
- core/kustomization.yaml
|
||||
- helm/kustomization.yaml
|
||||
- traefik/kustomization.yaml
|
||||
- gitops-ui/kustomization.yaml
|
||||
- monitoring/kustomization.yaml
|
||||
- longhorn-ui/kustomization.yaml
|
||||
|
||||
11
infrastructure/sources/helm/kustomization.yaml
Normal file
11
infrastructure/sources/helm/kustomization.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
# infrastructure/sources/helm/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- grafana.yaml
|
||||
- hashicorp.yaml
|
||||
- jetstack.yaml
|
||||
- mailu.yaml
|
||||
- prometheus.yaml
|
||||
- victoria-metrics.yaml
|
||||
- weave-gitops.yaml
|
||||
9
infrastructure/sources/helm/weave-gitops.yaml
Normal file
9
infrastructure/sources/helm/weave-gitops.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
# infrastructure/sources/helm/weave-gitops.yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: weave-gitops
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1h
|
||||
url: https://charts.gitops.weave.works
|
||||
@ -24,7 +24,7 @@ KC_CLIENT_ID = os.environ["KEYCLOAK_CLIENT_ID"]
|
||||
KC_CLIENT_SECRET = os.environ["KEYCLOAK_CLIENT_SECRET"]
|
||||
|
||||
MAILU_DOMAIN = os.environ["MAILU_DOMAIN"]
|
||||
MAILU_DEFAULT_QUOTA = int(os.environ.get("MAILU_DEFAULT_QUOTA", "1000000000"))
|
||||
MAILU_DEFAULT_QUOTA = int(os.environ.get("MAILU_DEFAULT_QUOTA", "20000000000"))
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": os.environ["MAILU_DB_HOST"],
|
||||
|
||||
49
services/gitops-ui/helmrelease.yaml
Normal file
49
services/gitops-ui/helmrelease.yaml
Normal file
@ -0,0 +1,49 @@
|
||||
# services/gitops-ui/helmrelease.yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: weave-gitops
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 30m
|
||||
chart:
|
||||
spec:
|
||||
chart: weave-gitops
|
||||
version: 4.0.36
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: weave-gitops
|
||||
namespace: flux-system
|
||||
install:
|
||||
remediation:
|
||||
retries: 3
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: 3
|
||||
remediateLastFailure: true
|
||||
cleanupOnFail: true
|
||||
values:
|
||||
adminUser:
|
||||
create: true
|
||||
createClusterRole: true
|
||||
createSecret: true
|
||||
username: admin
|
||||
# bcrypt hash for temporary password "G1tOps!2025" (rotate after login)
|
||||
passwordHash: "$2y$12$wDEOzR1Gc2dbvNSJ3ZXNdOBVFEjC6YASIxnZmHIbO.W1m0fie/QVi"
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
hosts:
|
||||
- host: cd.bstein.dev
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: gitops-ui-tls
|
||||
hosts:
|
||||
- cd.bstein.dev
|
||||
metrics:
|
||||
enabled: true
|
||||
6
services/gitops-ui/kustomization.yaml
Normal file
6
services/gitops-ui/kustomization.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
# services/gitops-ui/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: flux-system
|
||||
resources:
|
||||
- helmrelease.yaml
|
||||
@ -48,6 +48,20 @@ spec:
|
||||
runAsGroup: 0
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
imagePullSecrets:
|
||||
- name: zot-regcred
|
||||
initContainers:
|
||||
- name: mailu-http-listener
|
||||
image: registry.bstein.dev/sso/mailu-http-listener:0.1.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
cp /plugin/mailu-http-listener-0.1.0.jar /providers/
|
||||
cp -r /plugin/src /providers/src
|
||||
volumeMounts:
|
||||
- name: providers
|
||||
mountPath: /providers
|
||||
containers:
|
||||
- name: keycloak
|
||||
image: quay.io/keycloak/keycloak:26.0.7
|
||||
@ -104,6 +118,10 @@ spec:
|
||||
secretKeyRef:
|
||||
name: keycloak-admin
|
||||
key: password
|
||||
- name: KC_EVENTS_LISTENERS
|
||||
value: jboss-logging,mailu-http
|
||||
- name: KC_SPI_EVENTS_LISTENER_MAILU-HTTP_ENDPOINT
|
||||
value: http://mailu-sync-listener.mailu-mailserver.svc.cluster.local:8080/events
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
@ -126,7 +144,11 @@ spec:
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /opt/keycloak/data
|
||||
- name: providers
|
||||
mountPath: /opt/keycloak/providers
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: keycloak-data
|
||||
- name: providers
|
||||
emptyDir: {}
|
||||
|
||||
@ -32,9 +32,16 @@ spec:
|
||||
enabled: true
|
||||
dkim:
|
||||
enabled: true
|
||||
externalRelay:
|
||||
host: "[email-smtp.us-east-2.amazonaws.com]:587"
|
||||
existingSecret: mailu-ses-relay
|
||||
usernameKey: relay-username
|
||||
passwordKey: relay-password
|
||||
timezone: Etc/UTC
|
||||
subnet: 10.42.0.0/16
|
||||
existingSecret: mailu-secret
|
||||
tls:
|
||||
outboundLevel: encrypt
|
||||
externalDatabase:
|
||||
enabled: true
|
||||
type: postgresql
|
||||
@ -209,6 +216,10 @@ spec:
|
||||
logLevel: DEBUG
|
||||
nodeSelector:
|
||||
hardware: rpi4
|
||||
overrides:
|
||||
smtp_use_tls: "yes"
|
||||
smtp_tls_security_level: "encrypt"
|
||||
smtp_sasl_security_options: "noanonymous"
|
||||
redis:
|
||||
enabled: true
|
||||
architecture: standalone
|
||||
|
||||
@ -11,6 +11,8 @@ resources:
|
||||
- serverstransport.yaml
|
||||
- ingressroute.yaml
|
||||
- mailu-sync-job.yaml
|
||||
- mailu-sync-cronjob.yaml
|
||||
- mailu-sync-listener.yaml
|
||||
|
||||
configMapGenerator:
|
||||
- name: mailu-sync-script
|
||||
|
||||
77
services/mailu/mailu-sync-cronjob.yaml
Normal file
77
services/mailu/mailu-sync-cronjob.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
# services/mailu/mailu-sync-cronjob.yaml
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: mailu-sync-nightly
|
||||
namespace: mailu-mailserver
|
||||
spec:
|
||||
schedule: "30 4 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: mailu-sync
|
||||
image: python:3.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
pip install --no-cache-dir requests psycopg2-binary passlib >/tmp/pip.log \
|
||||
&& python /app/sync.py
|
||||
env:
|
||||
- name: KEYCLOAK_BASE_URL
|
||||
value: http://keycloak.sso.svc.cluster.local
|
||||
- name: KEYCLOAK_REALM
|
||||
value: atlas
|
||||
- name: MAILU_DOMAIN
|
||||
value: bstein.dev
|
||||
- name: MAILU_DEFAULT_QUOTA
|
||||
value: "20000000000"
|
||||
- name: MAILU_DB_HOST
|
||||
value: postgres-service.postgres.svc.cluster.local
|
||||
- name: MAILU_DB_PORT
|
||||
value: "5432"
|
||||
- name: MAILU_DB_NAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-db-secret
|
||||
key: database
|
||||
- name: MAILU_DB_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-db-secret
|
||||
key: username
|
||||
- name: MAILU_DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-db-secret
|
||||
key: password
|
||||
- name: KEYCLOAK_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-sync-credentials
|
||||
key: client-id
|
||||
- name: KEYCLOAK_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-sync-credentials
|
||||
key: client-secret
|
||||
volumeMounts:
|
||||
- name: sync-script
|
||||
mountPath: /app/sync.py
|
||||
subPath: sync.py
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
volumes:
|
||||
- name: sync-script
|
||||
configMap:
|
||||
name: mailu-sync-script
|
||||
defaultMode: 0444
|
||||
@ -25,7 +25,7 @@ spec:
|
||||
- name: MAILU_DOMAIN
|
||||
value: bstein.dev
|
||||
- name: MAILU_DEFAULT_QUOTA
|
||||
value: "1000000000"
|
||||
value: "20000000000"
|
||||
- name: MAILU_DB_HOST
|
||||
value: postgres-service.postgres.svc.cluster.local
|
||||
- name: MAILU_DB_PORT
|
||||
|
||||
154
services/mailu/mailu-sync-listener.yaml
Normal file
154
services/mailu/mailu-sync-listener.yaml
Normal file
@ -0,0 +1,154 @@
|
||||
# services/mailu/mailu-sync-listener.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mailu-sync-listener
|
||||
namespace: mailu-mailserver
|
||||
spec:
|
||||
selector:
|
||||
app: mailu-sync-listener
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mailu-sync-listener
|
||||
namespace: mailu-mailserver
|
||||
labels:
|
||||
app: mailu-sync-listener
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mailu-sync-listener
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mailu-sync-listener
|
||||
spec:
|
||||
restartPolicy: Always
|
||||
containers:
|
||||
- name: listener
|
||||
image: python:3.11-alpine
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
pip install --no-cache-dir requests psycopg2-binary passlib >/tmp/pip.log \
|
||||
&& python /app/listener.py
|
||||
env:
|
||||
- name: KEYCLOAK_BASE_URL
|
||||
value: http://keycloak.sso.svc.cluster.local
|
||||
- name: KEYCLOAK_REALM
|
||||
value: atlas
|
||||
- name: MAILU_DOMAIN
|
||||
value: bstein.dev
|
||||
- name: MAILU_DEFAULT_QUOTA
|
||||
value: "20000000000"
|
||||
- name: MAILU_DB_HOST
|
||||
value: postgres-service.postgres.svc.cluster.local
|
||||
- name: MAILU_DB_PORT
|
||||
value: "5432"
|
||||
- name: MAILU_DB_NAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-db-secret
|
||||
key: database
|
||||
- name: MAILU_DB_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-db-secret
|
||||
key: username
|
||||
- name: MAILU_DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-db-secret
|
||||
key: password
|
||||
- name: KEYCLOAK_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-sync-credentials
|
||||
key: client-id
|
||||
- name: KEYCLOAK_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mailu-sync-credentials
|
||||
key: client-secret
|
||||
volumeMounts:
|
||||
- name: sync-script
|
||||
mountPath: /app/sync.py
|
||||
subPath: sync.py
|
||||
- name: listener-script
|
||||
mountPath: /app/listener.py
|
||||
subPath: listener.py
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
volumes:
|
||||
- name: sync-script
|
||||
configMap:
|
||||
name: mailu-sync-script
|
||||
defaultMode: 0444
|
||||
- name: listener-script
|
||||
configMap:
|
||||
name: mailu-sync-listener
|
||||
defaultMode: 0444
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: mailu-sync-listener
|
||||
namespace: mailu-mailserver
|
||||
data:
|
||||
listener.py: |
|
||||
import http.server
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
from time import time
|
||||
|
||||
# Simple debounce to avoid hammering on bursts
|
||||
MIN_INTERVAL_SECONDS = 10
|
||||
last_run = 0.0
|
||||
lock = threading.Lock()
|
||||
|
||||
def trigger_sync():
|
||||
global last_run
|
||||
with lock:
|
||||
now = time()
|
||||
if now - last_run < MIN_INTERVAL_SECONDS:
|
||||
return
|
||||
last_run = now
|
||||
# Fire and forget; output to stdout
|
||||
subprocess.Popen(["python", "/app/sync.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
class Handler(http.server.BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
length = int(self.headers.get("Content-Length", 0))
|
||||
body = self.rfile.read(length) if length else b""
|
||||
try:
|
||||
json.loads(body or b"{}")
|
||||
except json.JSONDecodeError:
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
return
|
||||
trigger_sync()
|
||||
self.send_response(202)
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
# Quiet logging
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
server = http.server.ThreadingHTTPServer(("", 8080), Handler)
|
||||
server.serve_forever()
|
||||
Loading…
x
Reference in New Issue
Block a user