nextcloud: integration with mailu & gitops-ui: initial install
This commit is contained in:
parent
0d0216c8f5
commit
8fceebd7a7
@ -1,3 +1,3 @@
|
|||||||
# titan-iac
|
# 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
|
- core/kustomization.yaml
|
||||||
- helm/kustomization.yaml
|
- helm/kustomization.yaml
|
||||||
- traefik/kustomization.yaml
|
- traefik/kustomization.yaml
|
||||||
|
- gitops-ui/kustomization.yaml
|
||||||
- monitoring/kustomization.yaml
|
- monitoring/kustomization.yaml
|
||||||
- longhorn-ui/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"]
|
KC_CLIENT_SECRET = os.environ["KEYCLOAK_CLIENT_SECRET"]
|
||||||
|
|
||||||
MAILU_DOMAIN = os.environ["MAILU_DOMAIN"]
|
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 = {
|
DB_CONFIG = {
|
||||||
"host": os.environ["MAILU_DB_HOST"],
|
"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
|
runAsGroup: 0
|
||||||
fsGroup: 1000
|
fsGroup: 1000
|
||||||
fsGroupChangePolicy: OnRootMismatch
|
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:
|
containers:
|
||||||
- name: keycloak
|
- name: keycloak
|
||||||
image: quay.io/keycloak/keycloak:26.0.7
|
image: quay.io/keycloak/keycloak:26.0.7
|
||||||
@ -104,6 +118,10 @@ spec:
|
|||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: keycloak-admin
|
name: keycloak-admin
|
||||||
key: password
|
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:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
name: http
|
name: http
|
||||||
@ -126,7 +144,11 @@ spec:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /opt/keycloak/data
|
mountPath: /opt/keycloak/data
|
||||||
|
- name: providers
|
||||||
|
mountPath: /opt/keycloak/providers
|
||||||
volumes:
|
volumes:
|
||||||
- name: data
|
- name: data
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: keycloak-data
|
claimName: keycloak-data
|
||||||
|
- name: providers
|
||||||
|
emptyDir: {}
|
||||||
|
|||||||
@ -32,9 +32,16 @@ spec:
|
|||||||
enabled: true
|
enabled: true
|
||||||
dkim:
|
dkim:
|
||||||
enabled: true
|
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
|
timezone: Etc/UTC
|
||||||
subnet: 10.42.0.0/16
|
subnet: 10.42.0.0/16
|
||||||
existingSecret: mailu-secret
|
existingSecret: mailu-secret
|
||||||
|
tls:
|
||||||
|
outboundLevel: encrypt
|
||||||
externalDatabase:
|
externalDatabase:
|
||||||
enabled: true
|
enabled: true
|
||||||
type: postgresql
|
type: postgresql
|
||||||
@ -209,6 +216,10 @@ spec:
|
|||||||
logLevel: DEBUG
|
logLevel: DEBUG
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
hardware: rpi4
|
hardware: rpi4
|
||||||
|
overrides:
|
||||||
|
smtp_use_tls: "yes"
|
||||||
|
smtp_tls_security_level: "encrypt"
|
||||||
|
smtp_sasl_security_options: "noanonymous"
|
||||||
redis:
|
redis:
|
||||||
enabled: true
|
enabled: true
|
||||||
architecture: standalone
|
architecture: standalone
|
||||||
|
|||||||
@ -11,6 +11,8 @@ resources:
|
|||||||
- serverstransport.yaml
|
- serverstransport.yaml
|
||||||
- ingressroute.yaml
|
- ingressroute.yaml
|
||||||
- mailu-sync-job.yaml
|
- mailu-sync-job.yaml
|
||||||
|
- mailu-sync-cronjob.yaml
|
||||||
|
- mailu-sync-listener.yaml
|
||||||
|
|
||||||
configMapGenerator:
|
configMapGenerator:
|
||||||
- name: mailu-sync-script
|
- 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
|
- name: MAILU_DOMAIN
|
||||||
value: bstein.dev
|
value: bstein.dev
|
||||||
- name: MAILU_DEFAULT_QUOTA
|
- name: MAILU_DEFAULT_QUOTA
|
||||||
value: "1000000000"
|
value: "20000000000"
|
||||||
- name: MAILU_DB_HOST
|
- name: MAILU_DB_HOST
|
||||||
value: postgres-service.postgres.svc.cluster.local
|
value: postgres-service.postgres.svc.cluster.local
|
||||||
- name: MAILU_DB_PORT
|
- 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