Compare commits

..
64 Commits
Author SHA1 Message Date
jacob 33a91223a3 Provision Jacob database with pgvector through GitOps
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
2026-10-27 01:35:38 +02:00
Aleksandr Bogomiakov 87e7bcebcf Enable Hexor Lab login theme in Keycloak realm
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 19s
Keycloak Terraform / Terraform (push) Successful in 45s
2026-10-21 21:36:21 +01:00
Aleksandr Bogomiakov cd8cb1f586 Deploy Hexor Lab Phosphor login theme
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2026-10-21 21:31:25 +01:00
Ultradesu 729e808b13 Avoid Traefik NodePort collision with telemt TLS
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
2026-10-21 12:33:00 +01:00
Ultradesu af717cc530 Update k8s-secrets image digest for 0.2.2
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 7s
2026-10-21 12:10:56 +01:00
Ultradesu 56c4c3871a Sync k8s-secrets before monitoring wave completes
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
2026-10-21 11:54:18 +01:00
Ultradesu 8dd1868c25 Move k8s-secrets into k8s-ng and update reader image
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 6s
2026-10-21 11:52:13 +01:00
Ultradesu 5eaee8c7b7 Match ServiceMonitor relabel defaults
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
2026-10-21 11:38:14 +01:00
Ultradesu 7e7813bfe0 Mount telemt config as a regular file
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 6s
2026-10-21 11:36:08 +01:00
Ultradesu a79c695a40 Exclude unreachable home node from exporter rollout
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
2026-10-21 11:26:37 +01:00
Ultradesu 29d95cd853 Deploy MTProxy on external nodes via Argo CD
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 9s
2026-10-21 11:23:21 +01:00
Ultradesu 8a90756af0 Restore authenticated secret reader in current cluster
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 7s
Auto-update README / Generate README and Create MR (push) Successful in 7s
Check with kubeconform / lint (push) Successful in 10s
2026-10-18 11:23:14 +01:00
Ultradesu 52780edbb5 Expose authenticated Loki push endpoint for tsunagi logs
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
2026-10-18 00:35:03 +01:00
Ultradesu 8a4849733c Update Keycloak CI kubeconfig handling for current cluster
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
Keycloak Terraform / Terraform (push) Successful in 11s
2026-10-18 00:28:56 +01:00
Ultradesu 8e99f42815 Use active Kubernetes context in Keycloak Terraform CI
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 5s
Keycloak Terraform / Terraform (push) Failing after 11s
2026-10-18 00:26:58 +01:00
Ultradesu c4b608b621 Create tsunagi Vector machine client and Loki auth route
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Keycloak Terraform / Terraform (push) Failing after 21s
2026-10-18 00:23:48 +01:00
Aleksandr Bogomiakov dd0f664324 Add Shadowsocks deployment on spb with Vaultwarden credentials
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 6s
2026-10-15 10:58:52 +01:00
Aleksandr Bogomiakov 04610cc6e2 Changed llama model unsloth/Qwen3.8-27B-GGUF:UD-Q6_K -> unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q6_K
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 19s
2026-10-15 01:19:37 +01:00
Aleksandr Bogomiakov aaa1e9ae7e Run CoreDNS on the AI core node and prefer local DNS replicas
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 21s
2026-10-15 00:41:04 +01:00
Aleksandr Bogomiakov 47589988bf Prefer local ingress endpoints behind k3s ServiceLB
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 31s
2026-10-15 00:37:13 +01:00
Ultradesu 2a08898a23 Configure three CoreDNS replicas on core nodes
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 17s
2026-10-14 13:57:36 +01:00
Aleksandr Bogomiakov 9b0530fb70 Bound Telegram alert messages and identify affected workloads
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 9s
2026-10-12 18:23:21 +01:00
Aleksandr Bogomiakov ba1fcae807 Disabled some prom scrape configs
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2026-10-12 14:25:09 +01:00
Aleksandr Bogomiakov da97f66448 Keep ingress traffic on the node selected by DNS
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
2026-10-12 14:10:30 +01:00
Aleksandr Bogomiakov 2a89c89754 Run Traefik on every node, including the internal AI node
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 37s
2026-10-12 14:02:40 +01:00
Aleksandr Bogomiakov 8e4201621d Place core services and web-petting on external core nodes
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 1m6s
2026-10-12 13:44:16 +01:00
Aleksandr Bogomiakov ee7a82b0b8 Document PostgreSQL primary on music and replica on master
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 40s
2026-10-12 13:15:25 +01:00
Aleksandr Bogomiakov 6da0a39428 Preserve RustDesk client IP addresses during NAT detection
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 34s
2026-10-12 11:49:24 +01:00
Aleksandr Bogomiakov 4c744e539c Normalize migrated manifest whitespace
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 2m32s
2026-10-12 11:46:25 +01:00
Aleksandr Bogomiakov e94caf8054 Migrate Minecraft, RustDesk and Immich database to hexor-lab
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 3m3s
2026-10-12 11:45:52 +01:00
Aleksandr Bogomiakov 46bc178dab Move Vaultwarden local storage and workload to music
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 1m53s
2026-10-11 13:00:46 +01:00
Aleksandr Bogomiakov d1d28776cd Fix migration job reloads and alert context
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 17m22s
2026-10-08 18:00:44 +01:00
Aleksandr Bogomiakov 1991ffc1e3 Allow Loki gateway rolling updates on music
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 15s
2026-10-08 17:15:34 +01:00
Aleksandr Bogomiakov d8973e2daa Tune Bitwarden CLI CPU burst limit
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 19s
2026-10-08 17:11:31 +01:00
Aleksandr Bogomiakov f2d5b97465 Fixed alert reporting, instance label, gitea limits
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 29s
2026-10-08 17:03:46 +01:00
Aleksandr Bogomiakov ac8e5e127c NG: Fixed prom-stak
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 17s
2026-10-07 18:52:06 +01:00
Aleksandr Bogomiakov 52bfa88a33 Added prom-stack
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 31s
2026-10-07 18:35:17 +01:00
Aleksandr Bogomiakov 089bc9bfb5 Added meguri
Keycloak Terraform / Terraform (push) Failing after 41s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 2m59s
2026-10-07 18:09:48 +01:00
Aleksandr Bogomiakov 070176030c Adde meguri demo
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2026-10-07 14:24:11 +01:00
Aleksandr BogomiakovandClaude b6c671bc23 NG: bitwarden-cli bridge: liveness probe requires the unlocked status
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 31s
`bw serve` can lose its login while /sync still answers; every lookup then
fails with 500. The probe now also checks /status, so the pod restarts itself.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 21:04:29 +01:00
Aleksandr Bogomiakov 04c35083ef NG: moved gitea
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 27s
2026-10-06 20:26:25 +01:00
Aleksandr Bogomiakov be9ace99bc NG: moved gitea
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 29s
2026-10-06 20:20:49 +01:00
Aleksandr BogomiakovandClaude 1b41eee364 NG: gpu-system: drop the NFD nodeSelector the chart adds to the device plugin
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 23s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 19:27:35 +01:00
Aleksandr BogomiakovandClaude 373ce77ac8 NG: llamacpp on the ai node (Qwen3.8-27B) with the AMD GPU device plugin
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 44s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 19:20:06 +01:00
Aleksandr Bogomiakov bd88daa109 NG: fixed furumi DB
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 30s
2026-10-06 19:07:58 +01:00
Aleksandr BogomiakovandClaude 1c7185aed6 NG: Vaultwarden moves to vw.hexor.cy (vw.btwiusearch.net dropped)
Keycloak Terraform / Terraform (push) Failing after 32s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 16s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 18:05:41 +01:00
Aleksandr BogomiakovandClaude 6d39028e2f NG: HTTP-01 fallback solver for domains outside the DNS-01 zones
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 17s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 17:57:36 +01:00
Aleksandr BogomiakovandClaude 4159a6c045 NG: web-petting app
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 31s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 17:42:49 +01:00
Aleksandr BogomiakovandClaude 5f0a3ca3b6 NG: web_petting database in main PostgreSQL cluster
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 17:42:49 +01:00
Aleksandr BogomiakovandClaude 07cdc88568 NG: document PostgreSQL layout (primary on music, replica on ams-1)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 17:31:18 +01:00
Aleksandr BogomiakovandClaude b11b7c902d NG: move furumi to apps project
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 17s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 16:30:44 +01:00
Aleksandr BogomiakovandClaude ff4e87aca0 NG: furumi player pinned to the music node
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 1m19s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 16:18:36 +01:00
Aleksandr BogomiakovandClaude 01561ea2cd NG: furumi database in main PostgreSQL cluster
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-06 16:18:36 +01:00
Aleksandr Bogomiakov 7db427f332 NG: secret stores with lookup by path or id
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 20s
Move the Vaultwarden bridge and the ClusterSecretStores out of external-secrets
into secret-stores (wave 4, after vaultwarden). A store key may be an item UUID
or an item name used as a path (k8s/<namespace>/<what>), matched exactly.
2026-10-06 15:13:09 +01:00
Aleksandr Bogomiakov 41729bf256 NG: enable sso for VW
Keycloak Terraform / Terraform (push) Failing after 39s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 19s
2026-10-06 00:42:40 +01:00
Aleksandr Bogomiakov fe914e2de4 NG: enable sso for VW
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 28s
2026-10-06 00:18:56 +01:00
Aleksandr Bogomiakov 0348329829 NG: enable sso for argocd
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 15s
2026-10-06 00:11:02 +01:00
Aleksandr Bogomiakov e257e3895b NG: added Keycloak
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
2026-10-05 23:29:35 +01:00
Aleksandr Bogomiakov 4fe2a2b5f3 NG: added reloader
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 22s
2026-10-05 19:17:41 +01:00
Aleksandr Bogomiakov 690ff2a19c NG: added http -> https redir
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 17s
2026-10-05 19:00:28 +01:00
Aleksandr Bogomiakov 99e1818572 NG: app-of-apps CORE
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 24s
2026-10-05 18:49:42 +01:00
Aleksandr Bogomiakov cf88753ff5 NG: secret manager and VW
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Failing after 14m33s
2026-10-05 18:40:46 +01:00
Aleksandr Bogomiakov 862a7619e2 Track main instead of k8s-ng in the new cluster's applications
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 22s
2026-10-05 18:31:58 +01:00
Aleksandr Bogomiakov 5a29dcc236 Stop tracking rendered Helm charts in k8s-ng 2026-10-05 18:31:52 +01:00
358 changed files with 8517 additions and 82864 deletions

No files matched your search

+3 -1
View File
@@ -30,9 +30,11 @@ jobs:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- name: Setup kubeconfig
env:
CI_KUBECONFIG: ${{ secrets.KUBECONFIG }}
run: |
mkdir -p ~/.kube
echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config
printf '%s' "$CI_KUBECONFIG" > ~/.kube/config
chmod 600 ~/.kube/config
- name: Terraform Init
+3
View File
@@ -59,3 +59,6 @@ tags
[._]*.un~
.DS_Store
# Helm charts pulled by kustomize (--enable-helm) when rendering locally
k8s-ng/**/charts/
+50
View File
@@ -0,0 +1,50 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: furumi-player
labels:
app: furumi-player
spec:
replicas: 1
strategy:
type: Recreate # the library is a hostPath on a single node
selector:
matchLabels:
app: furumi-player
template:
metadata:
labels:
app: furumi-player
annotations:
reloader.stakater.com/auto: "true"
spec:
# The music library lives on this node's /data (LVM raid, not replicated).
nodeSelector:
kubernetes.io/hostname: music
containers:
- name: furumi-player
image: ultradesu/furumusic:latest
imagePullPolicy: Always
env:
- name: FURU_DATABASE_URL
valueFrom:
secretKeyRef:
name: furumi-creds
key: PG_STRING
ports:
- name: http
containerPort: 8000
volumeMounts:
- name: library
mountPath: /media
- name: inbox
mountPath: /inbox
volumes:
- name: library
hostPath:
path: /data/furumi/library
type: Directory
- name: inbox
hostPath:
path: /data/furumi/inbox
type: Directory
+24
View File
@@ -0,0 +1,24 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: furumi
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
ingressClassName: traefik
rules:
- host: music.hexor.cy
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: furumi-player
port:
number: 8000
tls:
- secretName: furumi-tls
hosts:
- music.hexor.cy
+9
View File
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- secrets.yaml
- deployment.yaml
- service.yaml
- servicemonitor.yaml
- ingress.yaml
+47
View File
@@ -0,0 +1,47 @@
# Read-only access to the furumi-db Secret in the psql namespace
# (see postgresql/databases/furumi.yaml for the other half).
apiVersion: v1
kind: ServiceAccount
metadata:
name: db-credentials-reader
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: psql-credentials
spec:
provider:
kubernetes:
remoteNamespace: psql
server:
caProvider:
type: ConfigMap
name: kube-root-ca.crt
key: ca.crt
auth:
serviceAccount:
name: db-credentials-reader
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: furumi-creds
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: psql-credentials
target:
name: furumi-creds
template:
data:
PG_STRING: "postgresql://{{ .username }}:{{ .password }}@main-rw.psql.svc:5432/furumi"
data:
- secretKey: username
remoteRef:
key: furumi-db
property: username
- secretKey: password
remoteRef:
key: furumi-db
property: password
+16
View File
@@ -0,0 +1,16 @@
---
apiVersion: v1
kind: Service
metadata:
name: furumi-player
labels:
app: furumi-player
spec:
type: ClusterIP
selector:
app: furumi-player
ports:
- name: http
protocol: TCP
port: 8000
targetPort: 8000
+13
View File
@@ -0,0 +1,13 @@
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: furumi-player
spec:
selector:
matchLabels:
app: furumi-player
endpoints:
- port: http
path: /metrics
interval: 30s
scrapeTimeout: 10s
+12
View File
@@ -0,0 +1,12 @@
# k8s-secrets
The `k8s-secrets` Argo CD Application reads this directory through
`core-state/values.yaml` and deploys into namespace `k8s-secret`.
External Secrets loads the `k8s/openai/creds` Vaultwarden item into
`openai-creds`. The reader at `pass.hexor.cy` can only read that Secret and is
protected by the Keycloak forward-auth proxy. Its access policy is managed in
`terraform/keycloak/terraform.tfvars`.
The Kubernetes resource names and namespace match the former deployment so
Argo CD can switch the source path without replacing the service or certificate.
@@ -0,0 +1,32 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: openai-creds
spec:
refreshInterval: 5m
secretStoreRef:
name: vaultwarden-login
kind: ClusterSecretStore
target:
name: openai-creds
creationPolicy: Owner
deletionPolicy: Retain
template:
type: Opaque
data:
USER: "{{ .user }}"
PASS: "{{ .pass }}"
TOTP: "{{ .totp }}"
data:
- secretKey: user
remoteRef:
key: k8s/openai/creds
property: login.username
- secretKey: pass
remoteRef:
key: k8s/openai/creds
property: login.password
- secretKey: totp
remoteRef:
key: k8s/openai/creds
property: login.totp
+38
View File
@@ -0,0 +1,38 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: auth-proxy
spec:
forwardAuth:
address: http://auth-proxy.keycloak.svc:80/auth
trustForwardHeader: false
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: secret-reader
spec:
entryPoints:
- websecure
routes:
- match: Host(`pass.hexor.cy`)
kind: Rule
middlewares:
- name: auth-proxy
services:
- name: secret-reader
port: 80
tls:
secretName: secret-reader-tls
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: secret-reader-tls
spec:
secretName: secret-reader-tls
issuerRef:
name: letsencrypt
kind: ClusterIssuer
dnsNames:
- pass.hexor.cy
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: k8s-secret
resources:
- external-secret.yaml
- reader.yaml
- ingress.yaml
+109
View File
@@ -0,0 +1,109 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: secret-reader
labels:
app: secret-reader
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["openai-creds"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secret-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: secret-reader
subjects:
- kind: ServiceAccount
name: secret-reader
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: secret-reader
labels:
app: secret-reader
spec:
replicas: 1
selector:
matchLabels:
app: secret-reader
template:
metadata:
labels:
app: secret-reader
spec:
serviceAccountName: secret-reader
nodeSelector:
homelab.hexor.cy/pool: core
homelab.hexor.cy/exposure: external
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: secret-reader
image: ultradesu/k8s-secrets:0.2.2@sha256:f206942d21ca9dcd131d6e19ba26cc17671815919217aef657b30a9f85854778
imagePullPolicy: IfNotPresent
args:
- --secrets
- openai-creds
- --namespace
- k8s-secret
- --port
- "3000"
ports:
- name: http
containerPort: 3000
env:
- name: RUST_LOG
value: info
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 150m
memory: 128Mi
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
---
apiVersion: v1
kind: Service
metadata:
name: secret-reader
labels:
app: secret-reader
spec:
selector:
app: secret-reader
ports:
- name: http
port: 80
targetPort: http
+18
View File
@@ -0,0 +1,18 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: llamacpp-config
data:
LLAMA_CACHE: /models
LLAMA_ARG_HOST: 0.0.0.0
LLAMA_ARG_PORT: "8080"
# The model is chosen here: <hf-user>/<repo>:<quant>. It is downloaded into
# /models on first start; changing it restarts the pod (reloader).
#LLAMA_ARG_HF_REPO: "unsloth/Qwen3.8-27B-GGUF:UD-Q6_K"
LLAMA_ARG_HF_REPO: "unsloth/Qwen3.6-35B-A3B-GGUF:UD-Q6_K"
LLAMA_ARG_CTX_SIZE: "131072"
LLAMA_ARG_N_GPU_LAYERS: all
LLAMA_ARG_FLASH_ATTN: "on"
LLAMA_ARG_FIT: "on"
# Strix Halo: mmap-ed weights hang or corrupt on ROCm; read them directly.
LLAMA_ARG_LOAD_MODE: dio
+76
View File
@@ -0,0 +1,76 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: llamacpp
annotations:
reloader.stakater.com/auto: "true"
labels:
app: llamacpp
spec:
replicas: 1
strategy:
type: Recreate # one GPU, one pod
selector:
matchLabels:
app: llamacpp
template:
metadata:
labels:
app: llamacpp
spec:
dnsPolicy: Default
nodeSelector:
homelab.hexor.cy/pool: core
homelab.hexor.cy/exposure: internal
workload/ai: "true"
tolerations:
- key: workload
operator: Equal
value: ai
effect: NoSchedule
containers:
- name: llamacpp
image: ghcr.io/ggml-org/llama.cpp:server-rocm-b11434
imagePullPolicy: IfNotPresent
envFrom:
- configMapRef:
name: llamacpp-config
env:
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: llamacpp-hf-token
key: token
optional: true
# hipBLASLt gives the biggest prompt-processing gain on ROCm 7.x.
- name: ROCBLAS_USE_HIPBLASLT
value: "1"
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
limits:
amd.com/gpu: 1
startupProbe:
httpGet:
path: /health
port: http
failureThreshold: 180 # first start downloads the model
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /health
port: http
failureThreshold: 3
periodSeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: models
mountPath: /models
volumes:
- name: models
hostPath:
path: /var/lib/llamacpp/models
type: DirectoryOrCreate
+7
View File
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- configmap.yaml
- deployment.yaml
- service.yaml
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: llamacpp
labels:
app: llamacpp
spec:
type: ClusterIP
selector:
app: llamacpp
ports:
- name: http
port: 8080
targetPort: http
protocol: TCP
+561
View File
@@ -0,0 +1,561 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: minecraft
data:
nginx.conf: |
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
# Custom 502 error page with auto-refresh
error_page 502 /502.html;
location = /502.html {
internal;
return 200 '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Server Loading</title><style>body{font-family:Arial,sans-serif;text-align:center;margin-top:100px;background:#f0f0f0}h1{color:#333}p{color:#666;font-size:18px}</style></head><body><h1>Server is loading probably...</h1><p>Please wait a moment and try refreshing the page.</p><script>setTimeout(function(){window.location.reload();}, 10000);</script></body></html>';
add_header Content-Type text/html;
}
# Main location - proxy to Minecraft Dynmap
location / {
# Proxy configuration for Dynmap server
proxy_pass http://localhost:8123;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Inject user authentication meta tag into HTML head
sub_filter '<head>' '<head><meta name="remote-user" content="$http_x_authentik_username">';
# Replace default Dynmap title with custom server name
sub_filter 'Minecraft Dynamic Map' "Hexor's MC server";
# Inject custom script before closing body tag
sub_filter "</body>" '<script src="/clients/inject.js"></script></body>';
# Apply sub_filter replacements globally (not just once)
sub_filter_once off;
}
# Serve inject.js and .ps1 scripts inline (no forced download)
location = /clients/inject.js {
alias /mc/clients/inject.js;
default_type application/javascript;
}
location ~ ^/clients/(.+\.ps1)$ {
alias /mc/clients/$1;
default_type text/plain;
}
# Static file serving for client downloads
location /clients/ {
alias /mc/clients/;
sendfile on; # Enable efficient file serving
add_header Content-Disposition "attachment"; # Force download
autoindex on; # Enable directory listing
gzip off; # Disable compression for downloads
chunked_transfer_encoding off; # Disable chunked encoding
}
}
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: client-scripts
namespace: minecraft
data:
inject.js: |
(function() {
function getUsername() {
var metas = document.querySelectorAll("meta");
for (var i = 0; i < metas.length; i++) {
if (metas[i].getAttribute("name") === "remote-user") {
return metas[i].getAttribute("content");
}
}
var jwt = document.cookie.split("; ").find(function(row) {
return row.startsWith("authentik_session=");
});
if (jwt) {
try {
var token = jwt.split("=")[1];
var payload = JSON.parse(atob(token.split(".")[1]));
return payload.sub || payload.username || "web-user";
} catch(e) {}
}
return "web-user";
}
var username = getUsername();
console.log("Username found:", username);
if (username && username !== "web-user" &&
window.location.search.indexOf("playername=") === -1) {
var currentUrl = new URL(window.location.href);
currentUrl.searchParams.set("playername", username);
console.log("Redirecting to:", currentUrl.href);
window.location.href = currentUrl.href;
}
document.addEventListener("DOMContentLoaded", function() {
// User block
var userBlock = document.createElement("div");
userBlock.style.cssText = "background-color:#CEC6CB;color:black;padding:8px;text-align:center;font-size:medium;border-radius:4px;position:absolute;top:10px;right:150px;max-width:200px;";
userBlock.innerHTML = "Logged in as: <b>" + username + "</b>";
document.body.appendChild(userBlock);
// Info block
var infoBlock = document.createElement("p");
infoBlock.style.cssText = "background-color:#CEC6CB;color:black;padding:10px;text-align:center;font-size:large;display:inline-block;border-radius:4px;position:absolute;top:10px;left:150px;";
infoBlock.innerHTML = 'GEYMERSKIY SOYUZ Server<br>'
+ 'Get <a href="https://github.com/PrismLauncher/PrismLauncher/releases/tag/8.4">Prism Launcher</a> '
+ 'and <a href="/clients/1.12.2.zip">client.zip</a> for this server. '
+ 'Server address <b>minecraft.hexor.cy:30565</b><br>'
+ 'Requires <a href="https://www.java.com/en/download/manual.jsp">Java 8</a><br><br>'
+ '<a href="#" id="showInstallBtn" style="color:black;text-decoration:underline;">Windows Install Script</a>';
document.body.appendChild(infoBlock);
// Modal
var modal = document.createElement("div");
modal.id = "installModal";
modal.style.cssText = "display:none;position:fixed;z-index:1000;left:0;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.5);";
modal.innerHTML = '<div style="background-color:#CEC6CB;margin:15% auto;padding:10px;border-radius:4px;width:70%;max-width:500px;text-align:center;color:black;font-size:large;">'
+ '<h3 style="margin-top:0;color:black;">Windows Installation</h3>'
+ '<p style="color:black;">Copy and paste this command into PowerShell:</p>'
+ '<textarea id="scriptCommand" readonly style="width:90%;height:60px;font-family:monospace;padding:8px;border:1px solid #888;border-radius:4px;resize:none;background-color:white;color:black;"></textarea>'
+ '<br><br>'
+ '<button id="copyButton" style="background-color:#CEC6CB;color:black;padding:10px 15px;border:1px solid #888;border-radius:4px;cursor:pointer;margin-right:10px;font-size:large;">Copy</button>'
+ '<button id="closeButton" style="background-color:#CEC6CB;color:black;padding:10px 15px;border:1px solid #888;border-radius:4px;cursor:pointer;font-size:large;">Close</button>'
+ '</div>';
document.body.appendChild(modal);
// Generate PowerShell command with username
function buildPsCommand() {
var d = "$";
var q = "'";
return d + 'f="' + d + 'env:TEMP\\mc-install.ps1"; iwr -useb https://minecraft.hexor.cy/clients/win-install.ps1 -OutFile '
+ d + 'f; powershell -ExecutionPolicy Bypass -File ' + d + 'f -Username ' + q + username + q + '; Remove-Item ' + d + 'f';
}
document.getElementById("showInstallBtn").addEventListener("click", function(e) {
e.preventDefault();
modal.style.display = "block";
document.getElementById("scriptCommand").value = buildPsCommand();
});
document.getElementById("closeButton").addEventListener("click", function() {
modal.style.display = "none";
});
document.getElementById("copyButton").addEventListener("click", function() {
var textarea = document.getElementById("scriptCommand");
textarea.select();
textarea.setSelectionRange(0, 99999);
document.execCommand("copy");
var btn = document.getElementById("copyButton");
btn.style.borderColor = "#4CAF50";
setTimeout(function() { btn.style.borderColor = "#888"; }, 2000);
});
modal.addEventListener("click", function(event) {
if (event.target === modal) {
modal.style.display = "none";
}
});
});
})();
win-install.ps1: |
# Game Setup Script for PrismLauncher and Minecraft Client
# This script downloads and configures PrismLauncher with Hexor client
param(
[string]$Username = "",
[string]$InstallPath = "$env:USERPROFILE\Games\PrismLauncher"
)
# Enable TLS 1.2 for downloads
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Function to write colored log messages
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO"
)
switch ($Level) {
"INFO" {
Write-Host "[" -NoNewline
Write-Host "INFO" -ForegroundColor Blue -NoNewline
Write-Host "] $Message"
}
"WARN" {
Write-Host "[" -NoNewline
Write-Host "WARN" -ForegroundColor Yellow -NoNewline
Write-Host "] $Message"
}
"ERROR" {
Write-Host "[" -NoNewline
Write-Host "ERROR" -ForegroundColor Red -NoNewline
Write-Host "] $Message"
}
"SUCCESS" {
Write-Host "[" -NoNewline
Write-Host "SUCCESS" -ForegroundColor Green -NoNewline
Write-Host "] $Message"
}
}
}
# Function to download file with progress
function Download-File {
param(
[string]$Url,
[string]$OutputPath
)
try {
Write-Log "Downloading from: $Url"
Write-Log "Saving to: $OutputPath"
if (Test-Path $OutputPath) {
Remove-Item $OutputPath -Force
Write-Log "Removed existing file to avoid corruption" "WARN"
}
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($Url, $OutputPath)
if (Test-Path $OutputPath) {
$fileSize = (Get-Item $OutputPath).Length
Write-Log "Download completed successfully ($fileSize bytes)" "SUCCESS"
return $true
} else {
Write-Log "Download failed - file not found" "ERROR"
return $false
}
}
catch {
Write-Log "Download failed: $($_.Exception.Message)" "ERROR"
return $false
}
}
# Function to extract ZIP archive
function Extract-Archive {
param(
[string]$ArchivePath,
[string]$DestinationPath
)
try {
Write-Log "Extracting archive: $ArchivePath"
Write-Log "Destination: $DestinationPath"
if (!(Test-Path $ArchivePath)) {
Write-Log "Archive file not found: $ArchivePath" "ERROR"
return $false
}
$fileSize = (Get-Item $ArchivePath).Length
if ($fileSize -lt 1000) {
Write-Log "Archive file too small ($fileSize bytes), probably corrupted" "ERROR"
return $false
}
if (!(Test-Path $DestinationPath)) {
New-Item -ItemType Directory -Path $DestinationPath -Force | Out-Null
}
$existingFiles = Get-ChildItem -Path $DestinationPath -File | Where-Object { $_.Extension -ne ".zip" }
$existingDirs = Get-ChildItem -Path $DestinationPath -Directory
if ($existingFiles.Count -gt 0 -or $existingDirs.Count -gt 0) {
Write-Log "Clearing existing files in destination directory" "WARN"
$existingFiles | Remove-Item -Force -ErrorAction SilentlyContinue
$existingDirs | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
try {
$zip = [System.IO.Compression.ZipFile]::OpenRead($ArchivePath)
$entryCount = $zip.Entries.Count
$zip.Dispose()
Write-Log "ZIP file validated ($entryCount entries)" "SUCCESS"
}
catch {
Write-Log "ZIP file validation failed: $($_.Exception.Message)" "ERROR"
return $false
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($ArchivePath, $DestinationPath)
Write-Log "Archive extracted successfully" "SUCCESS"
return $true
}
catch {
Write-Log "Archive extraction failed: $($_.Exception.Message)" "ERROR"
if (Test-Path $ArchivePath) {
Write-Log "Removing potentially corrupted archive file" "WARN"
Remove-Item $ArchivePath -Force -ErrorAction SilentlyContinue
}
return $false
}
}
# Function to check Java installation
function Check-JavaInstallation {
try {
$javaVersion = java -version 2>&1 | Select-String "version"
if ($javaVersion) {
Write-Log "Java is installed: $($javaVersion.ToString().Trim())" "SUCCESS"
return $true
}
}
catch {
Write-Log "Java is not installed or not in PATH" "WARN"
Write-Log "Please download Java from: https://www.java.com/en/download/manual.jsp" "WARN"
Write-Log "Look for 'Windows Offline (64-bit)' version" "WARN"
$response = Read-Host "Do you want to continue without Java? (y/n)"
if ($response -eq 'y' -or $response -eq 'Y') {
Write-Log "Continuing without Java verification" "WARN"
return $true
} else {
Write-Log "Installation cancelled. Please install Java first." "ERROR"
return $false
}
}
}
# Function to generate a random hex string (UUID-like without dashes)
function New-RandomHexId {
param([int]$Length = 32)
$bytes = New-Object byte[] ($Length / 2)
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rng.GetBytes($bytes)
$rng.Dispose()
return ($bytes | ForEach-Object { $_.ToString("x2") }) -join ''
}
# Function to create accounts.json file with offline account
function Create-AccountsFile {
param(
[string]$TargetPath,
[string]$PlayerName
)
$accountsPath = Join-Path $TargetPath "accounts.json"
$profileId = New-RandomHexId -Length 32
$clientToken = New-RandomHexId -Length 32
$iat = [int][double]::Parse((Get-Date -UFormat %s))
$accountsObj = @{
accounts = @(
@{
entitlement = @{
canPlayMinecraft = $true
ownsMinecraft = $true
}
"msa-client-id" = ""
type = "MSA"
},
@{
active = $true
entitlement = @{
canPlayMinecraft = $true
ownsMinecraft = $true
}
profile = @{
capes = @()
id = $profileId
name = $PlayerName
skin = @{
id = ""
url = ""
variant = ""
}
}
type = "Offline"
ygg = @{
extra = @{
clientToken = $clientToken
userName = $PlayerName
}
iat = $iat
token = "0"
}
}
)
formatVersion = 3
}
try {
$accountsObj | ConvertTo-Json -Depth 10 | Out-File -FilePath $accountsPath -Encoding UTF8
Write-Log "Created accounts.json at: $accountsPath" "SUCCESS"
Write-Log "Player name: $PlayerName" "SUCCESS"
return $true
}
catch {
Write-Log "Failed to create accounts.json: $($_.Exception.Message)" "ERROR"
return $false
}
}
# Main installation process
Write-Log "Starting PrismLauncher and Minecraft client setup"
Write-Log "Player name: $Username"
Write-Log "Installation path: $InstallPath"
# Create installation directory and clear if exists (but keep ZIP files)
if (Test-Path $InstallPath) {
Write-Log "Installation directory exists, clearing contents" "WARN"
$existingFiles = Get-ChildItem -Path $InstallPath -File | Where-Object { $_.Extension -ne ".zip" }
$existingDirs = Get-ChildItem -Path $InstallPath -Directory
$existingFiles | Remove-Item -Force -ErrorAction SilentlyContinue
$existingDirs | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
} else {
New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null
Write-Log "Created installation directory: $InstallPath"
}
# Step 1: Download PrismLauncher
Write-Log "Step 1: Downloading PrismLauncher..."
$launcherUrl = "https://github.com/PrismLauncher/PrismLauncher/releases/download/8.4/PrismLauncher-Windows-MSVC-Portable-8.4.zip"
$launcherZip = Join-Path $InstallPath "PrismLauncher-8.4.zip"
$launcherExtractPath = Join-Path $InstallPath "PrismLauncher-Windows-MSVC-Portable-8.4"
if (!(Download-File -Url $launcherUrl -OutputPath $launcherZip)) {
Write-Log "Failed to download PrismLauncher. Exiting." "ERROR"
exit 1
}
# Step 2: Extract PrismLauncher
Write-Log "Step 2: Extracting PrismLauncher..."
if (!(Extract-Archive -ArchivePath $launcherZip -DestinationPath $InstallPath)) {
Write-Log "Failed to extract PrismLauncher. Exiting." "ERROR"
exit 1
}
# Find the actual extracted directory
$extractedDirs = Get-ChildItem -Path $InstallPath -Directory | Where-Object { $_.Name -like "*PrismLauncher*" }
if ($extractedDirs.Count -gt 0) {
$launcherExtractPath = $extractedDirs[0].FullName
Write-Log "Found PrismLauncher directory: $launcherExtractPath" "SUCCESS"
} else {
Write-Log "Could not find extracted PrismLauncher directory. Checking for direct extraction..." "WARN"
$prismExe = Join-Path $InstallPath "prismlauncher.exe"
if (Test-Path $prismExe) {
$launcherExtractPath = $InstallPath
Write-Log "PrismLauncher extracted directly to: $launcherExtractPath" "SUCCESS"
} else {
Write-Log "Failed to locate PrismLauncher files. Exiting." "ERROR"
exit 1
}
}
# Step 3: Create accounts.json in InstallPath (only if username provided)
if ($Username -and $Username -ne "web-user") {
Write-Log "Step 3: Creating accounts configuration for $Username..."
if (!(Create-AccountsFile -TargetPath $InstallPath -PlayerName $Username)) {
Write-Log "Failed to create accounts.json. Exiting." "ERROR"
exit 1
}
} else {
Write-Log "Step 3: No username provided, skipping accounts.json creation" "WARN"
}
# Step 4: Check Java installation
Write-Log "Step 4: Checking Java installation..."
if (!(Check-JavaInstallation)) {
Write-Log "Java check failed. Exiting." "ERROR"
exit 1
}
# Step 5: Download Minecraft client
Write-Log "Step 5: Downloading Minecraft client..."
$minecraftUrl = "https://minecraft.hexor.cy/clients/1.12.2.zip"
$minecraftZip = Join-Path $InstallPath "minecraft-1.12.2.zip"
$instancesPath = Join-Path $launcherExtractPath "instances"
$hexorPath = Join-Path $instancesPath "Hexor"
if (!(Download-File -Url $minecraftUrl -OutputPath $minecraftZip)) {
Write-Log "Failed to download Minecraft client. Exiting." "ERROR"
exit 1
}
# Step 6: Create instances directory and extract Minecraft client
Write-Log "Step 6: Setting up Minecraft client..."
if (!(Test-Path $instancesPath)) {
New-Item -ItemType Directory -Path $instancesPath -Force | Out-Null
Write-Log "Created instances directory"
}
if (!(Test-Path $hexorPath)) {
New-Item -ItemType Directory -Path $hexorPath -Force | Out-Null
Write-Log "Created Hexor instance directory"
}
if (!(Extract-Archive -ArchivePath $minecraftZip -DestinationPath $hexorPath)) {
Write-Log "Failed to extract Minecraft client. Exiting." "ERROR"
exit 1
}
# Step 7: Cleanup temporary files
Write-Log "Step 7: Cleaning up temporary files..."
try {
Remove-Item $launcherZip -Force
Remove-Item $minecraftZip -Force
Write-Log "Temporary files cleaned up" "SUCCESS"
}
catch {
Write-Log "Could not remove temporary files: $($_.Exception.Message)" "WARN"
}
# Final success message
Write-Log "=== INSTALLATION COMPLETED SUCCESSFULLY ===" "SUCCESS"
Write-Log "PrismLauncher location: $launcherExtractPath" "SUCCESS"
Write-Log "Executable: $(Join-Path $launcherExtractPath 'prismlauncher.exe')" "SUCCESS"
Write-Log "Minecraft client installed in: $hexorPath" "SUCCESS"
if ($Username -and $Username -ne "web-user") {
Write-Log "Player name configured: $Username" "SUCCESS"
Write-Log "Accounts file: $(Join-Path $InstallPath 'accounts.json')" "SUCCESS"
}
Write-Log ""
Write-Log "You can now run PrismLauncher and the Hexor instance should be available!"
Write-Log "If Java was not installed, please download it from: https://www.java.com/en/download/manual.jsp"
# Ask if user wants to launch the game
$launchResponse = Read-Host "Do you want to launch PrismLauncher now? (y/n)"
if ($launchResponse -eq 'y' -or $launchResponse -eq 'Y') {
$launcherExe = Join-Path $launcherExtractPath "prismlauncher.exe"
if (Test-Path $launcherExe) {
Write-Log "Launching PrismLauncher..." "INFO"
Start-Process -FilePath $launcherExe -WorkingDirectory $launcherExtractPath
} else {
Write-Log "Launcher executable not found!" "ERROR"
}
}
+137
View File
@@ -0,0 +1,137 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: minecraft
namespace: minecraft
labels:
app: minecraft
spec:
selector:
matchLabels:
app: minecraft
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: minecraft
spec:
nodeSelector:
kubernetes.io/hostname: music
volumes:
- name: storage
hostPath:
path: /data/k8s/mc-server/
type: Directory
- name: nginx-config
configMap:
name: nginx-config
- name: client-scripts
configMap:
name: client-scripts
terminationGracePeriodSeconds: 180
containers:
- name: minecraft
image: eclipse-temurin:8-jdk-ubi10-minimal@sha256:85c943a7093a276b0f6ae13dee2ffecb98e390ab6a32fc7e4b8b1fc6bb103dd7
command: ["java"]
args:
- -Xms4G
- -Xmx4G
- -XX:+UseG1GC
- -XX:+ParallelRefProcEnabled
- -XX:MaxGCPauseMillis=200
- -XX:+UnlockExperimentalVMOptions
- -XX:+DisableExplicitGC
- -XX:+AlwaysPreTouch
- -XX:G1NewSizePercent=30
- -XX:G1MaxNewSizePercent=40
- -XX:G1HeapRegionSize=8M
- -XX:G1ReservePercent=20
- -XX:G1HeapWastePercent=5
- -XX:G1MixedGCCountTarget=4
- -XX:InitiatingHeapOccupancyPercent=15
- -XX:G1MixedGCLiveThresholdPercent=90
- -XX:G1RSetUpdatingPauseTimePercent=5
- -XX:SurvivorRatio=32
- -XX:+PerfDisableSharedMem
- -XX:MaxTenuringThreshold=1
- -jar
- forge-1.12.2-14.23.5.2854.jar
- nogui
workingDir: /mc/
resources:
requests:
memory: "8Gi"
cpu: "2000m"
limits:
memory: "12Gi"
cpu: "4000m"
ports:
- name: game
containerPort: 25565
protocol: TCP
- name: dynmap
containerPort: 8123
protocol: TCP
- name: webstatus-mod
containerPort: 8080
protocol: TCP
startupProbe:
tcpSocket:
port: game
periodSeconds: 10
failureThreshold: 90
livenessProbe:
tcpSocket:
port: game
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
tcpSocket:
port: game
periodSeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: storage
mountPath: /mc
- name: nginx
image: nginx@sha256:f9ea18bfa4fad859e1ed38259d711da7ccad2c3516e875cec3351a57c859f571
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: storage
mountPath: /mc
- name: client-scripts
mountPath: /mc/clients/win-install.ps1
subPath: win-install.ps1
- name: client-scripts
mountPath: /mc/clients/inject.js
subPath: inject.js
---
apiVersion: v1
kind: Service
metadata:
name: minecraft-exporter
namespace: minecraft
spec:
selector:
app: minecraft
ports:
- protocol: TCP
port: 19565
targetPort: 19565
+24
View File
@@ -0,0 +1,24 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minecraft
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
ingressClassName: traefik
rules:
- host: minecraft.hexor.cy
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: minecraft-dynmap
port:
number: 80
tls:
- secretName: minecraft-tls
hosts:
- minecraft.hexor.cy
+8
View File
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: minecraft
resources:
- deployments.yaml
- configmaps.yaml
- services.yaml
- ingress.yaml
+39
View File
@@ -0,0 +1,39 @@
---
apiVersion: v1
kind: Service
metadata:
name: minecraft-dynmap
spec:
selector:
app: minecraft
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: minecraft-webstatus-mod
spec:
selector:
app: minecraft
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: minecraft-game
spec:
type: LoadBalancer
externalTrafficPolicy: Cluster
ports:
- port: 30565
targetPort: 25565
selector:
app: minecraft
+23
View File
@@ -0,0 +1,23 @@
# MTProxy
Argo CD deploys this directory through the `mtproxy` entry in
`core-state/values.yaml`. Two telemt DaemonSets run on every node labeled
`homelab.hexor.cy/exposure=external`: classic MTProxy on 30443/TCP and
fake-TLS on 30444/TCP. The classic listener replaces the old amd64-only
`telegrammessenger/proxy` image so the arm64 `spb` node is covered.
Vaultwarden item `k8s/mtproxy/secret` must contain a custom field named
`Secret` with the existing 32-character hexadecimal MTProxy secret. External
Secrets renders both configurations from that field. Reloader restarts the
DaemonSets when it changes. Changing this value invalidates existing links.
`mtproxy-link-reconciler` lists external nodes every five minutes and creates
`mtproxy-links` and `telemt-links` Secrets. A node's public DNS name must be
`<node>.hexor.cy` and must point to that node. The reader at `proxy.hexor.cy`
has read-only access to those two Secrets and is protected by Keycloak's
forward auth; its access policy is in `terraform/keycloak/terraform.tfvars`.
The proxy Pods have no Kubernetes API access.
Before traffic is switched over, allow inbound 30443/TCP and 30444/TCP on
every external node and verify both DNS and connectivity on each node.
Neither the cluster nor host firewall is changed by these manifests.
+149
View File
@@ -0,0 +1,149 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: mtproxy-classic
labels:
app: mtproxy-classic
spec:
selector:
matchLabels:
app: mtproxy-classic
template:
metadata:
labels:
app: mtproxy-classic
annotations:
reloader.stakater.com/auto: "true"
spec:
nodeSelector:
homelab.hexor.cy/exposure: external
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: telemt
image: ghcr.io/telemt/telemt@sha256:b311c20596213a3941dcd11f499be982de74f554eecfffe57fab51c1bf80d50a
imagePullPolicy: IfNotPresent
workingDir: /run/telemt
args: ["/app/config.toml"]
ports:
- name: proxy
containerPort: 30443
hostPort: 30443
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
readinessProbe:
tcpSocket:
port: proxy
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: config
mountPath: /app/config.toml
subPath: classic.toml
readOnly: true
- name: workdir
mountPath: /run/telemt
volumes:
- name: config
secret:
secretName: mtproxy-config
defaultMode: 0444
items:
- key: classic.toml
path: classic.toml
- name: workdir
emptyDir:
medium: Memory
sizeLimit: 4Mi
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: mtproxy-tls
labels:
app: mtproxy-tls
spec:
selector:
matchLabels:
app: mtproxy-tls
template:
metadata:
labels:
app: mtproxy-tls
annotations:
reloader.stakater.com/auto: "true"
spec:
nodeSelector:
homelab.hexor.cy/exposure: external
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
fsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: telemt
image: ghcr.io/telemt/telemt@sha256:b311c20596213a3941dcd11f499be982de74f554eecfffe57fab51c1bf80d50a
imagePullPolicy: IfNotPresent
workingDir: /run/telemt
args: ["/app/config.toml"]
ports:
- name: proxy
containerPort: 30444
hostPort: 30444
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
readinessProbe:
tcpSocket:
port: proxy
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 256Mi
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: config
mountPath: /app/config.toml
subPath: tls.toml
readOnly: true
- name: workdir
mountPath: /run/telemt
volumes:
- name: config
secret:
secretName: mtproxy-config
defaultMode: 0444
items:
- key: tls.toml
path: tls.toml
- name: workdir
emptyDir:
medium: Memory
sizeLimit: 4Mi
+74
View File
@@ -0,0 +1,74 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: mtproxy-config
spec:
refreshInterval: 1h
secretStoreRef:
name: vaultwarden-fields
kind: ClusterSecretStore
target:
name: mtproxy-config
creationPolicy: Owner
deletionPolicy: Retain
template:
type: Opaque
data:
SECRET: "{{ .secret }}"
classic.toml: |
[general]
use_middle_proxy = true
log_level = "normal"
[general.modes]
classic = true
secure = false
tls = false
[server]
port = 30443
metrics_port = 9090
metrics_whitelist = ["0.0.0.0/0"]
[server.api]
enabled = false
[[server.listeners]]
ip = "0.0.0.0"
[access.users]
user = "{{ .secret }}"
tls.toml: |
[general]
use_middle_proxy = true
log_level = "normal"
[general.modes]
classic = false
secure = false
tls = true
[server]
port = 30444
metrics_port = 9090
metrics_whitelist = ["0.0.0.0/0"]
[server.api]
enabled = false
[[server.listeners]]
ip = "0.0.0.0"
[censorship]
tls_domain = "ya.ru"
mask = true
tls_emulation = true
tls_front_dir = "tlsfront"
[access.users]
user = "{{ .secret }}"
data:
- secretKey: secret
remoteRef:
key: k8s/mtproxy/secret
property: Secret
+38
View File
@@ -0,0 +1,38 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: mtproxy-auth
spec:
forwardAuth:
address: http://auth-proxy.keycloak.svc:80/auth
trustForwardHeader: false
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: mtproxy-links
spec:
entryPoints:
- websecure
routes:
- match: Host(`proxy.hexor.cy`)
kind: Rule
middlewares:
- name: mtproxy-auth
services:
- name: mtproxy-link-reader
port: 80
tls:
secretName: mtproxy-links-tls
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: mtproxy-links-tls
spec:
secretName: mtproxy-links-tls
issuerRef:
name: letsencrypt
kind: ClusterIssuer
dnsNames:
- proxy.hexor.cy
+16
View File
@@ -0,0 +1,16 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mtproxy
resources:
- external-secrets.yaml
- daemonsets.yaml
- metrics.yaml
- links.yaml
- reader.yaml
- ingress.yaml
configMapGenerator:
- name: mtproxy-link-reconciler
files:
- reconcile.py=link-reconciler.py
generatorOptions:
disableNameSuffixHash: true
+75
View File
@@ -0,0 +1,75 @@
"""Publish client links for every external node, using its public DNS name."""
import base64
import json
import urllib.error
import urllib.parse
import urllib.request
API = "https://kubernetes.default.svc"
NAMESPACE = open("/var/run/secrets/kubernetes.io/serviceaccount/namespace", encoding="utf-8").read().strip()
TOKEN = open("/var/run/secrets/kubernetes.io/serviceaccount/token", encoding="utf-8").read().strip()
CA = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
def request(method, path, body=None):
import ssl
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(
API + path,
data=data,
method=method,
headers={
"Authorization": "Bearer " + TOKEN,
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req, context=ssl.create_default_context(cafile=CA), timeout=15) as response:
return json.load(response)
def publish(name, links):
path = f"/api/v1/namespaces/{NAMESPACE}/secrets"
data = {node: base64.b64encode(link.encode()).decode() for node, link in links.items()}
try:
current = request("GET", f"{path}/{name}")
except urllib.error.HTTPError as error:
if error.code != 404:
raise
request("POST", path, {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": name}, "type": "Opaque", "data": data})
return
if current.get("data", {}) == data:
return
# PUT replaces the data map, removing links for nodes that are no longer external.
request("PUT", f"{path}/{name}", {
"apiVersion": "v1",
"kind": "Secret",
"metadata": {"name": name, "resourceVersion": current["metadata"]["resourceVersion"]},
"type": "Opaque",
"data": data,
})
secret = open("/config/SECRET", encoding="utf-8").read().strip()
if len(secret) != 32 or any(char not in "0123456789abcdefABCDEF" for char in secret):
raise ValueError("Vaultwarden Secret must be 32 hexadecimal characters")
selector = urllib.parse.quote("homelab.hexor.cy/exposure=external", safe="")
nodes = request("GET", f"/api/v1/nodes?labelSelector={selector}")["items"]
if not nodes:
raise ValueError("No external nodes found")
classic = {}
tls = {}
for node in nodes:
name = node["metadata"]["name"]
# DNS is maintained outside the cluster; no public IP needs to be stored on a Node.
host = f"{name}.hexor.cy"
classic[name] = f"tg://proxy?server={host}&port=30443&secret={secret}"
tls[name] = f"tg://proxy?server={host}&port=30444&secret=ee{secret}79612e7275"
publish("mtproxy-links", classic)
publish("telemt-links", tls)
print(f"Published MTProxy links for {len(nodes)} external nodes")
+108
View File
@@ -0,0 +1,108 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: mtproxy-link-reconciler
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: mtproxy-external-node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mtproxy-external-node-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mtproxy-external-node-reader
subjects:
- kind: ServiceAccount
name: mtproxy-link-reconciler
namespace: mtproxy
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: mtproxy-link-writer
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["mtproxy-links", "telemt-links"]
verbs: ["get", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mtproxy-link-writer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: mtproxy-link-writer
subjects:
- kind: ServiceAccount
name: mtproxy-link-reconciler
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: mtproxy-link-reconciler
spec:
schedule: "*/5 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 2
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
serviceAccountName: mtproxy-link-reconciler
restartPolicy: OnFailure
nodeSelector:
homelab.hexor.cy/exposure: external
securityContext:
runAsNonRoot: true
runAsUser: 65534
runAsGroup: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: reconcile
image: python:3.13-alpine3.22@sha256:e81548ac35b07a3bd4805f275107592ef458b1e893c0e04d45aedaa19416cca5
command: ["python", "/scripts/reconcile.py"]
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 128Mi
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: scripts
mountPath: /scripts
readOnly: true
- name: config
mountPath: /config
readOnly: true
volumes:
- name: scripts
configMap:
name: mtproxy-link-reconciler
- name: config
secret:
secretName: mtproxy-config
items:
- key: SECRET
path: SECRET
+65
View File
@@ -0,0 +1,65 @@
apiVersion: v1
kind: Service
metadata:
name: mtproxy-classic-metrics
labels:
app: mtproxy-classic
spec:
clusterIP: None
selector:
app: mtproxy-classic
ports:
- name: metrics
port: 9090
targetPort: metrics
---
apiVersion: v1
kind: Service
metadata:
name: mtproxy-tls-metrics
labels:
app: mtproxy-tls
spec:
clusterIP: None
selector:
app: mtproxy-tls
ports:
- name: metrics
port: 9090
targetPort: metrics
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: mtproxy-classic
spec:
selector:
matchLabels:
app: mtproxy-classic
endpoints:
- port: metrics
path: /metrics
interval: 30s
scrapeTimeout: 10s
relabelings:
- action: replace
sourceLabels: [__meta_kubernetes_pod_node_name]
targetLabel: node
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: mtproxy-tls
spec:
selector:
matchLabels:
app: mtproxy-tls
endpoints:
- port: metrics
path: /metrics
interval: 30s
scrapeTimeout: 10s
relabelings:
- action: replace
sourceLabels: [__meta_kubernetes_pod_node_name]
targetLabel: node
+96
View File
@@ -0,0 +1,96 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: mtproxy-link-reader
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: mtproxy-link-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["mtproxy-links", "telemt-links"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mtproxy-link-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: mtproxy-link-reader
subjects:
- kind: ServiceAccount
name: mtproxy-link-reader
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mtproxy-link-reader
labels:
app: mtproxy-link-reader
spec:
replicas: 1
selector:
matchLabels:
app: mtproxy-link-reader
template:
metadata:
labels:
app: mtproxy-link-reader
spec:
serviceAccountName: mtproxy-link-reader
nodeSelector:
homelab.hexor.cy/exposure: external
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: reader
image: ultradesu/k8s-secrets:0.2.2@sha256:f206942d21ca9dcd131d6e19ba26cc17671815919217aef657b30a9f85854778
imagePullPolicy: IfNotPresent
args:
- --secrets
- mtproxy-links,telemt-links
- --namespace
- mtproxy
- --port
- "3000"
ports:
- name: http
containerPort: 3000
readinessProbe:
httpGet:
path: /health
port: http
livenessProbe:
httpGet:
path: /health
port: http
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 128Mi
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
---
apiVersion: v1
kind: Service
metadata:
name: mtproxy-link-reader
spec:
selector:
app: mtproxy-link-reader
ports:
- name: http
port: 80
targetPort: http
+151
View File
@@ -0,0 +1,151 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rustdesk-hbbs
annotations:
reloader.stakater.com/auto: "true"
labels:
app: rustdesk-hbbs
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: rustdesk-hbbs
template:
metadata:
labels:
app: rustdesk-hbbs
spec:
nodeSelector:
kubernetes.io/hostname: music
containers:
- name: hbbs
image: rustdesk/rustdesk-server@sha256:8ecdab65deb7c84652a626380e31d11a8f1fbafd97916d57f95c20628f943c00
imagePullPolicy: Always
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "750m"
command: ["hbbs"]
args:
- "--relay-servers"
- "rd.hexor.cy:21117"
- "--port"
- "21116"
ports:
- name: registry
containerPort: 21116
protocol: TCP
- name: registry-udp
containerPort: 21116
protocol: UDP
- name: nat
containerPort: 21115
protocol: TCP
readinessProbe:
tcpSocket:
port: registry
volumeMounts:
- name: keys
mountPath: /data
readOnly: true
- name: data
mountPath: /data-persistent
env:
- name: RUST_LOG
value: "info"
- name: DB_URL
value: "/data-persistent/db_v2.sqlite3"
workingDir: /data
volumes:
- name: keys
secret:
secretName: rustdesk-keys
items:
- key: id_ed25519
path: id_ed25519
mode: 0600
- key: id_ed25519.pub
path: id_ed25519.pub
mode: 0644
- name: data
hostPath:
path: /data/k8s/rustdesk/hbbs
type: Directory
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rustdesk-hbbr
annotations:
reloader.stakater.com/auto: "true"
labels:
app: rustdesk-hbbr
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: rustdesk-hbbr
template:
metadata:
labels:
app: rustdesk-hbbr
spec:
nodeSelector:
kubernetes.io/hostname: music
containers:
- name: hbbr
image: rustdesk/rustdesk-server@sha256:8ecdab65deb7c84652a626380e31d11a8f1fbafd97916d57f95c20628f943c00
imagePullPolicy: Always
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "750m"
command: ["hbbr"]
args:
- "--port"
- "21117"
ports:
- name: relay
containerPort: 21117
protocol: TCP
readinessProbe:
tcpSocket:
port: relay
volumeMounts:
- name: keys
mountPath: /data
readOnly: true
- name: data
mountPath: /data-persistent
env:
- name: RUST_LOG
value: "info"
workingDir: /data
volumes:
- name: keys
secret:
secretName: rustdesk-keys
items:
- key: id_ed25519
path: id_ed25519
mode: 0600
- key: id_ed25519.pub
path: id_ed25519.pub
mode: 0644
- name: data
hostPath:
path: /data/k8s/rustdesk/hbbr
type: Directory
@@ -0,0 +1,27 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: rustdesk-keys
spec:
refreshInterval: 1h
secretStoreRef:
name: vaultwarden-fields
kind: ClusterSecretStore
target:
name: rustdesk-keys
creationPolicy: Owner
deletionPolicy: Retain
template:
type: Opaque
data:
id_ed25519: "{{ .private_key }}"
id_ed25519.pub: "{{ .public_key }}"
data:
- secretKey: private_key
remoteRef:
key: k8s/rustdesk/secrets
property: Cert
- secretKey: public_key
remoteRef:
key: k8s/rustdesk/secrets
property: Key
+8
View File
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: rustdesk
resources:
- external-secrets.yaml
- deployment.yaml
- service.yaml
- network-policy.yaml
+73
View File
@@ -0,0 +1,73 @@
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: rustdesk-network-policy
spec:
podSelector:
matchLabels:
app: rustdesk-hbbs
policyTypes:
- Ingress
- Egress
ingress:
# Allow all incoming connections to RustDesk ports
- from: []
ports:
- protocol: TCP
port: 21115
- protocol: TCP
port: 21116
- protocol: UDP
port: 21116
egress:
# Allow DNS
- to: []
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# Allow communication between HBBS and HBBR
- to:
- podSelector:
matchLabels:
app: rustdesk-hbbr
ports:
- protocol: TCP
port: 21117
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: rustdesk-hbbr-network-policy
spec:
podSelector:
matchLabels:
app: rustdesk-hbbr
policyTypes:
- Ingress
- Egress
ingress:
# Allow all incoming connections to relay port
- from: []
ports:
- protocol: TCP
port: 21117
# Allow connections from HBBS
- from:
- podSelector:
matchLabels:
app: rustdesk-hbbs
ports:
- protocol: TCP
port: 21117
egress:
# Allow DNS
- to: []
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
+59
View File
@@ -0,0 +1,59 @@
# Preserve client source IPs for RustDesk NAT detection. Public DNS points to music,
# where both servers and their persistent data live.
---
apiVersion: v1
kind: Service
metadata:
name: rustdesk-hbbs
labels:
app: rustdesk-hbbs
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: rustdesk-hbbs
ports:
- name: registry-tcp
port: 21116
targetPort: 21116
protocol: TCP
- name: nat
port: 21115
targetPort: 21115
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: rustdesk-hbbs-udp
labels:
app: rustdesk-hbbs
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: rustdesk-hbbs
ports:
- name: registry-udp
port: 21116
targetPort: 21116
protocol: UDP
---
apiVersion: v1
kind: Service
metadata:
name: rustdesk-hbbr
labels:
app: rustdesk-hbbr
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: rustdesk-hbbr
ports:
- name: relay
port: 21117
targetPort: 21117
protocol: TCP
+18
View File
@@ -0,0 +1,18 @@
# Shadowsocks on spb
One shadowsocks-rust pod runs on `spb`, exposing `8388/TCP` and `8388/UDP`
through host ports. Client settings:
- Server: `spb.hexor.cy`
- Port: `8388`
- Method: `chacha20-ietf-poly1305`
- Password: the `Password` custom field of the Vaultwarden item named
`k8s/shadowsocks/secrets` (exact name, not an item UUID).
Create that item in the server secrets vault accessible to the External Secrets
bot. Use a long random password, for example generated with `openssl rand -base64 32`.
External Secrets creates `shadowsocks-config`; Reloader restarts the deployment
when the Secret changes. No persistent storage is required.
The spb NixOS configuration opens port 8388 for both protocols. Apply that
configuration when deploying this application for the first time.
+71
View File
@@ -0,0 +1,71 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: shadowsocks
annotations:
reloader.stakater.com/auto: "true"
labels:
app: shadowsocks
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: shadowsocks
template:
metadata:
labels:
app: shadowsocks
spec:
nodeSelector:
kubernetes.io/hostname: spb
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 65534
runAsGroup: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: shadowsocks
# shadowsocks-rust v1.25.0; multi-arch image includes linux/arm64.
image: ghcr.io/shadowsocks/ssserver-rust:v1.25.0@sha256:22240c07d8ce3a36421ede770bd775dcc5fc56d5d829943196957b05452dc36f
imagePullPolicy: IfNotPresent
command: ["/usr/bin/ssserver"]
args:
- --log-without-time
- -c
- /etc/shadowsocks-rust/config.json
ports:
- name: proxy-tcp
containerPort: 8388
hostPort: 8388
protocol: TCP
- name: proxy-udp
containerPort: 8388
hostPort: 8388
protocol: UDP
readinessProbe:
tcpSocket:
port: proxy-tcp
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: config
mountPath: /etc/shadowsocks-rust
readOnly: true
volumes:
- name: config
secret:
secretName: shadowsocks-config
defaultMode: 0444
@@ -0,0 +1,29 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: shadowsocks-config
spec:
refreshInterval: 1h
secretStoreRef:
name: vaultwarden-fields
kind: ClusterSecretStore
target:
name: shadowsocks-config
creationPolicy: Owner
deletionPolicy: Retain
template:
type: Opaque
data:
config.json: |
{
"server": "0.0.0.0",
"server_port": 8388,
"method": "chacha20-ietf-poly1305",
"password": {{ .password | toJson }},
"mode": "tcp_and_udp"
}
data:
- secretKey: password
remoteRef:
key: k8s/shadowsocks/secrets
property: Password
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shadowsocks
resources:
- external-secrets.yaml
- deployment.yaml
+54
View File
@@ -0,0 +1,54 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-petting
labels:
app: web-petting
spec:
replicas: 1
selector:
matchLabels:
app: web-petting
template:
metadata:
labels:
app: web-petting
annotations:
reloader.stakater.com/auto: "true"
spec:
nodeSelector:
homelab.hexor.cy/pool: core
homelab.hexor.cy/exposure: external
containers:
- name: web-petting
image: ultradesu/web-petting:latest
imagePullPolicy: Always
args:
- web-petting
- -l
- 0.0.0.0:3000
ports:
- name: http
containerPort: 3000
env:
- name: RUST_LOG
value: info
- name: WEB_PETTING_DEBUG
value: "false"
- name: WEB_PETTING_DATABASE_URL
valueFrom:
secretKeyRef:
name: web-petting-secrets
key: WEB_PETTING_DATABASE_URL
# Everything persistent is in PostgreSQL and R2; /data is scratch space only.
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
memory: 256Mi
limits:
memory: 1Gi
volumes:
- name: data
emptyDir: {}
+37
View File
@@ -0,0 +1,37 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-petting
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
ingressClassName: traefik
rules:
- host: pet.hexor.cy
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-petting
port:
number: 80
- host: xn--l1acako8eb.xn--p1ai
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-petting
port:
number: 80
tls:
- secretName: web-petting-tls
hosts:
- pet.hexor.cy
- secretName: web-petting-murnyanya-tls
hosts:
- xn--l1acako8eb.xn--p1ai
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- secrets.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
+47
View File
@@ -0,0 +1,47 @@
# Read-only access to the web-petting-db Secret in the psql namespace
# (see postgresql/databases/web-petting.yaml for the other half).
apiVersion: v1
kind: ServiceAccount
metadata:
name: db-credentials-reader
---
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: psql-credentials
spec:
provider:
kubernetes:
remoteNamespace: psql
server:
caProvider:
type: ConfigMap
name: kube-root-ca.crt
key: ca.crt
auth:
serviceAccount:
name: db-credentials-reader
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: web-petting-secrets
spec:
refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: psql-credentials
target:
name: web-petting-secrets
template:
data:
WEB_PETTING_DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@main-rw.psql.svc:5432/web_petting"
data:
- secretKey: username
remoteRef:
key: web-petting-db
property: username
- secretKey: password
remoteRef:
key: web-petting-db
property: password
+15
View File
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: web-petting
labels:
app: web-petting
spec:
type: ClusterIP
selector:
app: web-petting
ports:
- name: http
port: 80
targetPort: 3000
protocol: TCP
-21
View File
@@ -1,21 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argocd
namespace: argocd
spec:
project: core
destination:
namespace: argocd
server: https://kubernetes.default.svc
source:
repoURL: https://gt.hexor.cy/ab/homelab.git
targetRevision: k8s-ng
path: k8s-ng/argocd
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
@@ -1,4 +0,0 @@
/*.tgz
output
ci/
*.gotmpl
@@ -1,6 +0,0 @@
dependencies:
- name: redis-ha
repository: https://dandydeveloper.github.io/charts/
version: 4.38.0
digest: sha256:0d8b0161248c5ab2c77d62fc77d5ca28393f73f7b7a8abd5474b78bc969cab39
generated: "2026-06-15T10:27:45.019725+02:00"
@@ -1,31 +0,0 @@
annotations:
artifacthub.io/changes: |
- kind: added
description: Prevent high Redis' sentinel CPU usage after rolling update
artifacthub.io/signKey: |
fingerprint: 2B8F22F57260EFA67BE1C5824B11F800CD9D2252
url: https://argoproj.github.io/argo-helm/pgp_keys.asc
apiVersion: v2
appVersion: v3.5.2
dependencies:
- condition: redis-ha.enabled
name: redis-ha
repository: https://dandydeveloper.github.io/charts/
version: 4.38.0
description: A Helm chart for Argo CD, a declarative, GitOps continuous delivery tool
for Kubernetes.
home: https://github.com/argoproj/argo-helm
icon: https://argo-cd.readthedocs.io/en/stable/assets/logo.png
keywords:
- argoproj
- argocd
- gitops
kubeVersion: '>=1.25.0-0'
maintainers:
- name: argoproj
url: https://argoproj.github.io/
name: argo-cd
sources:
- https://github.com/argoproj/argo-cd
- https://github.com/argoproj/argo-helm/tree/main/charts/argo-cd
version: 10.6.0
File diff suppressed because it is too large. Load diff
@@ -1,25 +0,0 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
ci/
*.gotmpl
@@ -1,19 +0,0 @@
apiVersion: v2
appVersion: 8.8.0
description: This Helm chart provides a highly available Redis implementation with
a master/slave configuration and uses Sentinel sidecars for failover management
home: https://redis.io/
icon: https://img.icons8.com/external-tal-revivo-shadow-tal-revivo/24/external-redis-an-in-memory-data-structure-project-implementing-a-distributed-logo-shadow-tal-revivo.png
keywords:
- redis
- keyvalue
- database
maintainers:
- email: aaron.layfield@gmail.com
name: dandydeveloper
name: redis-ha
sources:
- https://redis.io/download
- https://github.com/DandyDeveloper/charts/blob/master/charts/redis-ha
- https://github.com/oliver006/redis_exporter
version: 4.38.0
@@ -1,500 +0,0 @@
# Redis
[Redis](http://redis.io/) is an advanced key-value cache and store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets, sorted sets, bitmaps and hyperloglogs.
## TL;DR
```bash
helm repo add dandydev https://dandydeveloper.github.io/charts
helm install dandydev/redis-ha
```
By default this chart install 3 pods total:
* one pod containing a redis master and sentinel container (optional prometheus metrics exporter sidecar available)
* two pods each containing a redis slave and sentinel containers (optional prometheus metrics exporter sidecars available)
## Introduction
This chart bootstraps a [Redis](https://redis.io) highly available master/slave statefulset in a [Kubernetes](http://kubernetes.io) cluster using the Helm package manager.
## Prerequisites
* Kubernetes 1.8+ with Beta APIs enabled
* PV provisioner support in the underlying infrastructure
* Helm v3+
## Upgrading the Chart
Please note that there have been a number of changes simplifying the redis management strategy (for better failover and elections) in the 3.x version of this chart. These changes allow the use of official [redis](https://hub.docker.com/_/redis/) images that do not require special RBAC or ServiceAccount roles. As a result when upgrading from version >=2.0.1 to >=3.0.0 of this chart, `Role`, `RoleBinding`, and `ServiceAccount` resources should be deleted manually.
### Upgrading the chart from 3.x to 4.x
Starting from version `4.x` HAProxy sidecar prometheus-exporter removed and replaced by the embedded [HAProxy metrics endpoint](https://github.com/haproxy/haproxy/tree/master/contrib/prometheus-exporter), as a result when upgrading from version 3.x to 4.x section `haproxy.exporter` should be removed and the `haproxy.metrics` need to be configured for fit your needs.
## Installing the Chart
To install the chart
```bash
helm repo add dandydev https://dandydeveloper.github.io/charts
helm install dandydev/redis-ha
```
The command deploys Redis on the Kubernetes cluster in the default configuration. By default this chart install one master pod containing redis master container and sentinel container along with 2 redis slave pods each containing their own sentinel sidecars. The [configuration](#configuration) section lists the parameters that can be configured during installation.
> **Tip**: List all releases using `helm list`
## Uninstalling the Chart
To uninstall/delete the deployment:
```bash
helm delete <chart-name>
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Configuration
The following table lists the configurable parameters of the Redis chart and their default values.
### General parameters
| Parameter | Description | Type | Default |
|-----|------|---------|-------------|
| `additionalAffinities` | Additional affinities to add to the Redis server pods. # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity | object | `{}` |
| `affinity` | Override all other affinity settings for the Redis server pods with a string. | string | `""` |
| `auth` | Configures redis with AUTH (requirepass & masterauth conf params) | bool | `false` |
| `authKey` | Defines the key holding the redis password in existing secret. | string | `"auth"` |
| `authSecretAnnotations` | Annotations for auth secret | object | `{}` |
| `configmap.labels` | Custom labels for the redis configmap | object | `{}` |
| `configmapTest.image` | Image for redis-ha-configmap-test hook | object | `{"repository":"koalaman/shellcheck","tag":"v0.11.0"}` |
| `configmapTest.image.repository` | Repository of the configmap shellcheck test image. | string | `"koalaman/shellcheck"` |
| `configmapTest.image.tag` | Tag of the configmap shellcheck test image. | string | `"v0.11.0"` |
| `configmapTest.resources` | Resources for the ConfigMap test pod | object | `{}` |
| `containerSecurityContext` | Security context to be added to the Redis containers. | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `emptyDir` | Configuration of `emptyDir`, used only if persistentVolume is disabled and no hostPath specified | object | `{}` |
| `existingSecret` | An existing secret containing a key defined by `authKey` that configures `requirepass` and `masterauth` in the conf parameters (Requires `auth: enabled`, cannot be used in conjunction with `.Values.redisPassword`) | string | `nil` |
| `extraContainers` | Extra containers to include in StatefulSet | list | `[]` |
| `extraInitContainers` | Extra init containers to include in StatefulSet | list | `[]` |
| `extraLabels` | Labels added here are applied to all created resources | object | `{}` |
| `extraVolumes` | Extra volumes to include in StatefulSet | list | `[]` |
| `fullnameOverride` | Full name of the Redis HA Resources | string | `""` |
| `global.compatibility` | Openshift compatibility options | object | `{"openshift":{"adaptSecurityContext":"auto"}}` |
| `global.priorityClassName` | Default priority class for all components | string | `""` |
| `hardAntiAffinity` | Whether the Redis server pods should be forced to run on separate nodes. # This is accomplished by setting their AntiAffinity with requiredDuringSchedulingIgnoredDuringExecution as opposed to preferred. # Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#inter-pod-affinity-and-anti-affinity-beta-feature | bool | `true` |
| `hostPath.chown` | if chown is true, an init-container with root permissions is launched to change the owner of the hostPath folder to the user defined in the security context | bool | `true` |
| `hostPath.path` | Use this path on the host for data storage. path is evaluated as template so placeholders are replaced | string | `""` |
| `image.pullPolicy` | Redis image pull policy | string | `"IfNotPresent"` |
| `image.repository` | Redis image repository | string | `"public.ecr.aws/docker/library/redis"` |
| `image.tag` | Redis image tag | string | `"8.8.0-alpine"` |
| `imagePullSecrets` | Reference to one or more secrets to be used when pulling redis images | list | `[]` |
| `init.resources` | Extra init resources | object | `{}` |
| `labels` | Custom labels for the redis pod | object | `{}` |
| `nameOverride` | Name override for Redis HA resources | string | `""` |
| `networkPolicy.annotations` | Annotations for NetworkPolicy | object | `{}` |
| `networkPolicy.egressRules` | user can define egress rules too, uses the same structure as ingressRules | list | `[{"ports":[{"port":53,"protocol":"UDP"},{"port":53,"protocol":"TCP"}],"selectors":[{"namespaceSelector":{}},{"ipBlock":{"cidr":"169.254.0.0/16"}}]}]` |
| `networkPolicy.egressRules[0].selectors[0]` | Allow all destinations for DNS traffic | object | `{"namespaceSelector":{}}` |
| `networkPolicy.enabled` | whether NetworkPolicy for Redis StatefulSets should be created. when enabled, inter-Redis connectivity is created | bool | `false` |
| `networkPolicy.ingressRules` | User defined ingress rules that Redis should permit into. Uses the format defined in https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors | list | `[]` |
| `networkPolicy.labels` | Labels for NetworkPolicy | object | `{}` |
| `nodeSelector` | Node labels for pod assignment | object | `{}` |
| `persistentVolume.accessModes` | Persistent volume access modes | list | `["ReadWriteOnce"]` |
| `persistentVolume.annotations` | Annotations for the volume | object | `{}` |
| `persistentVolume.enabled` | Enable persistent volume | bool | `true` |
| `persistentVolume.labels` | Labels for the volume | object | `{}` |
| `persistentVolume.size` | Persistent volume size | string | `"10Gi"` |
| `persistentVolume.storageClass` | redis-ha data Persistent Volume Storage Class | string | `nil` |
| `podDisruptionBudget` | Pod Disruption Budget rules | object | `{}` |
| `podManagementPolicy` | The statefulset pod management policy | string | `"OrderedReady"` |
| `priorityClassName` | Kubernetes priorityClass name for the redis-ha-server pod | string | `""` |
| `rbac.create` | Create and use RBAC resources | bool | `true` |
| `redis.annotations` | Annotations for the redis statefulset | object | `{}` |
| `redis.authClients` | It is possible to disable client side certificates authentication when "authClients" is set to "no" | string | `""` |
| `redis.config` | Any valid redis config options in this section will be applied to each server, For multi-value configs use list instead of string (for example loadmodule) (see below) | object | see values.yaml |
| `redis.config.maxmemory` | Max memory to use for each redis instance. Default is unlimited. | string | `"0"` |
| `redis.config.maxmemory-policy` | Max memory policy to use for each redis instance. Default is volatile-lru. | string | `"volatile-lru"` |
| `redis.config.min-replicas-max-lag` | Value in seconds | int | `5` |
| `redis.config.repl-diskless-sync` | When enabled, directly sends the RDB over the wire to slaves, without using the disk as intermediate storage. Default is false. | string | `"yes"` |
| `redis.config.save` | Please note that local (on-disk) RDBs will still be created when re-syncing with a new slave. The only way to prevent this is to enable diskless replication. | string | `"900 1"` |
| `redis.customArgs` | Allows overriding the redis container arguments | list | `[]` |
| `redis.customCommand` | Allows overriding the redis container command | list | `[]` |
| `redis.customConfig` | Allows for custom redis.conf files to be applied. If this is used then `redis.config` is ignored | string | `nil` |
| `redis.disableCommands` | Array with commands to disable | list | `["FLUSHDB","FLUSHALL"]` |
| `redis.envFrom` | Load environment variables from ConfigMap/Secret | list | `[]` |
| `redis.extraVolumeMounts` | additional volumeMounts for Redis container | list | `[]` |
| `redis.lifecycle` | Container Lifecycle Hooks for redis container Ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ | object | see values.yaml |
| `redis.livenessProbe` | Liveness probe parameters for redis container | object | `{"enabled":true,"failureThreshold":5,"initialDelaySeconds":30,"periodSeconds":15,"successThreshold":1,"timeoutSeconds":15}` |
| `redis.livenessProbe.enabled` | Enable the Liveness Probe | bool | `true` |
| `redis.livenessProbe.failureThreshold` | Failure threshold for liveness probe | int | `5` |
| `redis.livenessProbe.initialDelaySeconds` | Initial delay in seconds for liveness probe | int | `30` |
| `redis.livenessProbe.periodSeconds` | Period in seconds after which liveness probe will be repeated | int | `15` |
| `redis.livenessProbe.successThreshold` | Success threshold for liveness probe | int | `1` |
| `redis.livenessProbe.timeoutSeconds` | Timeout seconds for liveness probe | int | `15` |
| `redis.masterGroupName` | Redis convention for naming the cluster group: must match `^[\\w-\\.]+$` and can be templated | string | `"mymaster"` |
| `redis.minReadySeconds` | Configure the 'minReadySeconds' parameter to StatefulSet ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#minreadyseconds | int | `0` |
| `redis.podAnnotations` | Annotations to be added to the redis statefulset pods | object | `{}` |
| `redis.port` | Port to access the redis service | int | `6379` |
| `redis.readinessProbe` | Readiness probe parameters for redis container | object | `{"enabled":true,"failureThreshold":5,"initialDelaySeconds":30,"periodSeconds":15,"successThreshold":1,"timeoutSeconds":15}` |
| `redis.readinessProbe.enabled` | Enable the Readiness Probe | bool | `true` |
| `redis.readinessProbe.failureThreshold` | Failure threshold for readiness probe | int | `5` |
| `redis.readinessProbe.initialDelaySeconds` | Initial delay in seconds for readiness probe | int | `30` |
| `redis.readinessProbe.periodSeconds` | Period in seconds after which readiness probe will be repeated | int | `15` |
| `redis.readinessProbe.successThreshold` | Success threshold for readiness probe | int | `1` |
| `redis.readinessProbe.timeoutSeconds` | Timeout seconds for readiness probe | int | `15` |
| `redis.resources` | CPU/Memory for master/slave nodes resource requests/limits | object | `{}` |
| `redis.startupProbe` | Startup probe parameters for redis container | object | `{"enabled":true,"failureThreshold":5,"initialDelaySeconds":30,"periodSeconds":15,"successThreshold":1,"timeoutSeconds":15}` |
| `redis.startupProbe.enabled` | Enable Startup Probe | bool | `true` |
| `redis.startupProbe.failureThreshold` | Failure threshold for startup probe | int | `5` |
| `redis.startupProbe.initialDelaySeconds` | Initial delay in seconds for startup probe | int | `30` |
| `redis.startupProbe.periodSeconds` | Period in seconds after which startup probe will be repeated | int | `15` |
| `redis.startupProbe.successThreshold` | Success threshold for startup probe | int | `1` |
| `redis.startupProbe.timeoutSeconds` | Timeout seconds for startup probe | int | `15` |
| `redis.terminationGracePeriodSeconds` | Increase terminationGracePeriodSeconds to allow writing large RDB snapshots. (k8s default is 30s) ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination-forced | int | `60` |
| `redis.tlsPort` | TLS Port to access the redis service | int | `nil` |
| `redis.tlsReplication` | Configures redis with tls-replication parameter, if true sets "tls-replication yes" in redis.conf | bool | `nil` |
| `redis.updateStrategy` | Update strategy for Redis StatefulSet # ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies | object | `{"type":"RollingUpdate"}` |
| `redisPassword` | A password that configures a `requirepass` and `masterauth` in the conf parameters (Requires `auth: enabled`) | string | `nil` |
| `replicas` | Number of redis master/slave | int | `3` |
| `restore.existingSecret` | Set existingSecret to true to use secret specified in existingSecret above | bool | `false` |
| `restore.redis.source` | | string | `""` |
| `restore.s3.access_key` | Restore init container - AWS AWS_ACCESS_KEY_ID to access restore.s3.source | string | `""` |
| `restore.s3.region` | Restore init container - AWS AWS_REGION to access restore.s3.source | string | `""` |
| `restore.s3.secret_key` | Restore init container - AWS AWS_SECRET_ACCESS_KEY to access restore.s3.source | string | `""` |
| `restore.s3.source` | Restore init container - AWS S3 location of dump - i.e. s3://bucket/dump.rdb or false | string | `""` |
| `restore.ssh.key` | Restore init container - SSH private key to scp restore.ssh.source to init container. Key should be in one line separated with \n. i.e. `-----BEGIN RSA PRIVATE KEY-----\n...\n...\n-----END RSA PRIVATE KEY-----` | string | `""` |
| `restore.ssh.source` | Restore init container - SSH scp location of dump - i.e. user@server:/path/dump.rdb or false | string | `""` |
| `restore.timeout` | Timeout for the restore | int | `600` |
| `ro_replicas` | Comma separated list of slaves which never get promoted to be master. Count starts with 0. Allowed values 1-9. i.e. 3,4 - 3th and 4th redis slave never make it to be master, where master is index 0. | string | `""` |
| `schedulerName` | Use an alternate scheduler, e.g. "stork". ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ | string | `""` |
| `securityContext` | Security context to be added to the Redis StatefulSet. | object | `{"fsGroup":1000,"runAsNonRoot":true,"runAsUser":1000}` |
| `serviceAccount.annotations` | Annotations to be added to the service account for the redis statefulset | object | `{}` |
| `serviceAccount.automountToken` | opt in/out of automounting API credentials into container. Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ | bool | `false` |
| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | bool | `true` |
| `serviceAccount.name` | The name of the ServiceAccount to use. If not set and create is true, a name is generated using the redis-ha.fullname template | string | `""` |
| `serviceLabels` | Custom labels for redis service | object | `{}` |
| `splitBrainDetection.interval` | Interval between redis sentinel and server split brain checks (in seconds) | int | `60` |
| `splitBrainDetection.resources` | splitBrainDetection resources | object | `{}` |
| `splitBrainDetection.retryInterval` | | int | `10` |
| `sysctlImage.command` | sysctlImage command to execute | list | `[]` |
| `sysctlImage.enabled` | Enable an init container to modify Kernel settings | bool | `false` |
| `sysctlImage.mountHostSys` | Mount the host `/sys` folder to `/host-sys` | bool | `false` |
| `sysctlImage.pullPolicy` | sysctlImage Init container pull policy | string | `"Always"` |
| `sysctlImage.registry` | sysctlImage Init container registry | string | `"public.ecr.aws/docker/library"` |
| `sysctlImage.repository` | sysctlImage Init container name | string | `"busybox"` |
| `sysctlImage.resources` | sysctlImage resources | object | `{}` |
| `sysctlImage.tag` | sysctlImage Init container tag | string | `"1.38.0"` |
| `tls.caCertFile` | Name of CA certificate file | string | `"ca.crt"` |
| `tls.certFile` | Name of certificate file | string | `"redis.crt"` |
| `tls.dhParamsFile` | Name of Diffie-Hellman (DH) key exchange parameters file (Example: redis.dh) | string | `nil` |
| `tls.keyFile` | Name of key file | string | `"redis.key"` |
| `tolerations` | | list | `[]` |
| `topologySpreadConstraints.enabled` | Enable topology spread constraints | bool | `false` |
| `topologySpreadConstraints.maxSkew` | Max skew of pods tolerated | string | `""` |
| `topologySpreadConstraints.topologyKey` | Topology key for spread constraints | string | `""` |
| `topologySpreadConstraints.whenUnsatisfiable` | Enforcement policy, hard or soft | string | `""` |
### Redis Sentinel parameters
| Parameter | Description | Type | Default |
|-----|------|---------|-------------|
| `sentinel.auth` | Enables or disables sentinel AUTH (Requires `sentinel.password` to be set) | bool | `false` |
| `sentinel.authClients` | It is possible to disable client side certificates authentication when "authClients" is set to "no" | string | `""` |
| `sentinel.authKey` | The key holding the sentinel password in an existing secret. | string | `"sentinel-password"` |
| `sentinel.config` | Valid sentinel config options in this section will be applied as config options to each sentinel (see below) | object | see values.yaml |
| `sentinel.customArgs` | | list | `[]` |
| `sentinel.customCommand` | | list | `[]` |
| `sentinel.customConfig` | Allows for custom sentinel.conf files to be applied. If this is used then `sentinel.config` is ignored | string | `""` |
| `sentinel.existingSecret` | An existing secret containing a key defined by `sentinel.authKey` that configures `requirepass` in the conf parameters (Requires `sentinel.auth: enabled`, cannot be used in conjunction with `.Values.sentinel.password`) | string | `""` |
| `sentinel.extraVolumeMounts` | additional volumeMounts for Sentinel container | list | `[]` |
| `sentinel.lifecycle` | Container Lifecycle Hooks for sentinel container. Ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ | object | `{}` |
| `sentinel.livenessProbe.enabled` | | bool | `true` |
| `sentinel.livenessProbe.failureThreshold` | Failure threshold for liveness probe | int | `5` |
| `sentinel.livenessProbe.initialDelaySeconds` | Initial delay in seconds for liveness probe | int | `30` |
| `sentinel.livenessProbe.periodSeconds` | Period in seconds after which liveness probe will be repeated | int | `15` |
| `sentinel.livenessProbe.successThreshold` | Success threshold for liveness probe | int | `1` |
| `sentinel.livenessProbe.timeoutSeconds` | Timeout seconds for liveness probe | int | `15` |
| `sentinel.password` | A password that configures a `requirepass` in the conf parameters (Requires `sentinel.auth: enabled`) | string | `nil` |
| `sentinel.resolveHostnames` | Configures sentinel with resolve-hostnames parameter, if true sets "resolve-hostnames yes" in sentinel.conf | bool | `nil` |
| `sentinel.announceHostnames` | Configures sentinel with announce-hostnames parameter, if true sets "announce-hostnames yes" in sentinel.conf | bool | `nil` |
| `sentinel.port` | Port to access the sentinel service | int | `26379` |
| `sentinel.quorum` | Minimum number of nodes expected to be live. | int | `2` |
| `sentinel.readinessProbe.enabled` | | bool | `true` |
| `sentinel.readinessProbe.failureThreshold` | Failure threshold for readiness probe | int | `5` |
| `sentinel.readinessProbe.initialDelaySeconds` | Initial delay in seconds for readiness probe | int | `30` |
| `sentinel.readinessProbe.periodSeconds` | Period in seconds after which readiness probe will be repeated | int | `15` |
| `sentinel.readinessProbe.successThreshold` | Success threshold for readiness probe | int | `3` |
| `sentinel.readinessProbe.timeoutSeconds` | Timeout seconds for readiness probe | int | `15` |
| `sentinel.resources` | CPU/Memory for sentinel node resource requests/limits | object | `{}` |
| `sentinel.startupProbe` | Startup probe parameters for redis container | object | `{"enabled":true,"failureThreshold":3,"initialDelaySeconds":5,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":15}` |
| `sentinel.startupProbe.enabled` | Enable Startup Probe | bool | `true` |
| `sentinel.startupProbe.failureThreshold` | Failure threshold for startup probe | int | `3` |
| `sentinel.startupProbe.initialDelaySeconds` | Initial delay in seconds for startup probe | int | `5` |
| `sentinel.startupProbe.periodSeconds` | Period in seconds after which startup probe will be repeated | int | `10` |
| `sentinel.startupProbe.successThreshold` | Success threshold for startup probe | int | `1` |
| `sentinel.startupProbe.timeoutSeconds` | Timeout seconds for startup probe | int | `15` |
| `sentinel.tlsPort` | TLS Port to access the sentinel service | int | `nil` |
| `sentinel.tlsReplication` | Configures sentinel with tls-replication parameter, if true sets "tls-replication yes" in sentinel.conf | bool | `nil` |
### HAProxy parameters
| Parameter | Description | Type | Default |
|-----|------|---------|-------------|
| `haproxy.IPv6.enabled` | Enable HAProxy parameters to bind and consume IPv6 addresses. Enabled by default. | bool | `true` |
| `haproxy.additionalAffinities` | Additional affinities to add to the haproxy pods. | object | `{}` |
| `haproxy.additionalPorts` | Additional ports to expose on HAProxy service and deployment. Each port should have a name, containerPort, and optionally servicePort (defaults to containerPort) | list | `[]` |
| `haproxy.affinity` | Override all other affinity settings for the haproxy pods with a string. | string | `""` |
| `haproxy.annotations` | HAProxy template annotations | object | `{}` |
| `haproxy.checkFall` | haproxy.cfg `check fall` setting | int | `1` |
| `haproxy.checkInterval` | haproxy.cfg `check inter` setting | string | `"1s"` |
| `haproxy.containerPort` | Modify HAProxy deployment container port | int | `6379` |
| `haproxy.containerSecurityContext` | Security context to be added to the HAProxy containers. | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"runAsNonRoot":true,"seccompProfile":{"type":"RuntimeDefault"}}` |
| `haproxy.customConfig` | Allows for custom config-haproxy.cfg file to be applied. If this is used then default config will be overwriten | string | `nil` |
| `haproxy.deploymentAnnotations` | HAProxy deployment annotations | object | `{}` |
| `haproxy.deploymentStrategy` | Deployment strategy for the haproxy deployment | object | `{"type":"RollingUpdate"}` |
| `haproxy.emptyDir` | Configuration of `emptyDir` | object | `{}` |
| `haproxy.enabled` | Enabled HAProxy LoadBalancing/Proxy | bool | `false` |
| `haproxy.extraConfig` | Allows to place any additional configuration section to add to the default config-haproxy.cfg | string | `nil` |
| `haproxy.hardAntiAffinity` | Whether the haproxy pods should be forced to run on separate nodes. | bool | `true` |
| `haproxy.image.pullPolicy` | HAProxy Image PullPolicy | string | `"IfNotPresent"` |
| `haproxy.image.repository` | HAProxy Image Repository | string | `"public.ecr.aws/docker/library/haproxy"` |
| `haproxy.image.tag` | HAProxy Image Tag | string | `"3.0.8-alpine"` |
| `haproxy.imagePullSecrets` | Reference to one or more secrets to be used when pulling images ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ | list | `[]` |
| `haproxy.init.resources` | Extra init resources | object | `{}` |
| `haproxy.labels` | Custom labels for the haproxy pod | object | `{}` |
| `haproxy.lifecycle` | Container lifecycle hooks. Ref: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ | object | `{}` |
| `haproxy.metrics.enabled` | HAProxy enable prometheus metric scraping | bool | `false` |
| `haproxy.metrics.port` | HAProxy prometheus metrics scraping port | int | `9101` |
| `haproxy.metrics.portName` | HAProxy metrics scraping port name | string | `"http-exporter-port"` |
| `haproxy.metrics.scrapePath` | HAProxy prometheus metrics scraping path | string | `"/metrics"` |
| `haproxy.metrics.serviceMonitor.disableAPICheck` | Disable API Check on ServiceMonitor | bool | `false` |
| `haproxy.metrics.serviceMonitor.enabled` | When set true then use a ServiceMonitor to configure scraping | bool | `false` |
| `haproxy.metrics.serviceMonitor.endpointAdditionalProperties` | Set additional properties for the ServiceMonitor endpoints such as relabeling, scrapeTimeout, tlsConfig, and more. | object | `{}` |
| `haproxy.metrics.serviceMonitor.interval` | Set how frequently Prometheus should scrape (default is 30s) | string | `""` |
| `haproxy.metrics.serviceMonitor.labels` | Set labels for the ServiceMonitor, use this to define your scrape label for Prometheus Operator | object | `{}` |
| `haproxy.metrics.serviceMonitor.namespace` | Set the namespace the ServiceMonitor should be deployed | string | `.Release.Namespace` |
| `haproxy.metrics.serviceMonitor.telemetryPath` | Set path to redis-exporter telemtery-path (default is /metrics) | string | `""` |
| `haproxy.metrics.serviceMonitor.timeout` | Set timeout for scrape (default is 10s) | string | `""` |
| `haproxy.networkPolicy.annotations` | Annotations for Haproxy NetworkPolicy | object | `{}` |
| `haproxy.networkPolicy.egressRules` | user can define egress rules too, uses the same structure as ingressRules | list | `[]` |
| `haproxy.networkPolicy.enabled` | whether NetworkPolicy for Haproxy should be created | bool | `false` |
| `haproxy.networkPolicy.ingressRules` | user defined ingress rules that Haproxy should permit into. uses the format defined in https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors | list | `[]` |
| `haproxy.networkPolicy.labels` | Labels for Haproxy NetworkPolicy | object | `{}` |
| `haproxy.podAnnotations` | Annotations to be added to the HAProxy deployment pods | object | `{}` |
| `haproxy.podDisruptionBudget` | Pod Disruption Budget ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ | object | `{}` |
| `haproxy.priorityClassName` | Kubernetes priorityClass name for the haproxy pod | string | `""` |
| `haproxy.readOnly` | Enable read-only redis-slaves | object | `{"enabled":false,"port":6380}` |
| `haproxy.readOnly.enabled` | Enable if you want a dedicated port in haproxy for redis-slaves | bool | `false` |
| `haproxy.readOnly.port` | Port for the read-only redis-slaves | int | `6380` |
| `haproxy.replicas` | Number of HAProxy instances | int | `3` |
| `haproxy.resources` | HAProxy resources | object | `{}` |
| `haproxy.securityContext` | Security context to be added to the HAProxy deployment. | object | `{"fsGroup":99,"runAsNonRoot":true,"runAsUser":99}` |
| `haproxy.service.annotations` | HAProxy service annotations | string | `nil` |
| `haproxy.service.externalIPs` | HAProxy external IPs | object | `{}` |
| `haproxy.service.externalTrafficPolicy` | HAProxy service externalTrafficPolicy value (haproxy.service.type must be LoadBalancer) | string | `nil` |
| `haproxy.service.labels` | HAProxy service labels | object | `{}` |
| `haproxy.service.loadBalancerIP` | HAProxy service loadbalancer IP | string | `nil` |
| `haproxy.service.loadBalancerSourceRanges` | List of CIDR's allowed to connect to LoadBalancer | list | `[]` |
| `haproxy.service.nodePort` | HAProxy service nodePort value (haproxy.service.type must be NodePort) | int | `nil` |
| `haproxy.service.type` | HAProxy service type "ClusterIP", "LoadBalancer" or "NodePort" | string | `"ClusterIP"` |
| `haproxy.serviceAccount.automountToken` | | bool | `true` |
| `haproxy.serviceAccount.create` | Specifies whether a ServiceAccount should be created | bool | `true` |
| `haproxy.serviceAccountName` | HAProxy serviceAccountName | string | `"redis-sa"` |
| `haproxy.servicePort` | Modify HAProxy service port | int | `6379` |
| `haproxy.stickyBalancing` | HAProxy sticky load balancing to Redis nodes. Helps with connections shutdown. | bool | `false` |
| `haproxy.tests.resources` | Pod resources for the tests against HAProxy. | object | `{}` |
| `haproxy.timeout.check` | haproxy.cfg `timeout check` setting | string | `"2s"` |
| `haproxy.timeout.client` | haproxy.cfg `timeout client` setting | string | `"330s"` |
| `haproxy.timeout.connect` | haproxy.cfg `timeout connect` setting | string | `"4s"` |
| `haproxy.timeout.server` | haproxy.cfg `timeout server` setting | string | `"330s"` |
| `haproxy.timeout.tunnel` | haproxy.cfg `timeout tunnel` setting | string | `"1h"` |
| `haproxy.tls` | Enable TLS termination on HAproxy, This will create a volume mount | object | `{"certMountPath":"/tmp/","enabled":false,"keyName":null,"secretName":""}` |
| `haproxy.tls.certMountPath` | Path to mount the secret that contains the certificates. haproxy | string | `"/tmp/"` |
| `haproxy.tls.enabled` | If "true" this will enable TLS termination on haproxy | bool | `false` |
| `haproxy.tls.keyName` | Key file name | string | `nil` |
| `haproxy.tls.secretName` | Secret containing the .pem file | string | `""` |
### Prometheus exporter parameters
| Parameter | Description | Type | Default |
|-----|------|---------|-------------|
| `exporter.address` | Address/Host for Redis instance. Exists to circumvent issues with IPv6 dns resolution that occurs on certain environments | string | `"localhost"` |
| `exporter.enabled` | If `true`, the prometheus exporter sidecar is enabled | bool | `false` |
| `exporter.extraArgs` | Additional args for redis exporter | object | `{}` |
| `exporter.image` | Exporter image | string | `"quay.io/oliver006/redis_exporter"` |
| `exporter.livenessProbe.httpGet.path` | Exporter liveness probe httpGet path | string | `"/metrics"` |
| `exporter.livenessProbe.httpGet.port` | Exporter liveness probe httpGet port | int | `9121` |
| `exporter.livenessProbe.initialDelaySeconds` | Initial delay in seconds for liveness probe of exporter | int | `15` |
| `exporter.livenessProbe.periodSeconds` | Period in seconds after which liveness probe will be repeated | int | `15` |
| `exporter.livenessProbe.timeoutSeconds` | Timeout seconds for liveness probe of exporter | int | `3` |
| `exporter.port` | Exporter port | int | `9121` |
| `exporter.portName` | Exporter port name | string | `"exporter-port"` |
| `exporter.pullPolicy` | Exporter image pullPolicy | string | `"IfNotPresent"` |
| `exporter.readinessProbe.httpGet.path` | Exporter readiness probe httpGet path | string | `"/metrics"` |
| `exporter.readinessProbe.httpGet.port` | Exporter readiness probe httpGet port | int | `9121` |
| `exporter.readinessProbe.initialDelaySeconds` | Initial delay in seconds for readiness probe of exporter | int | `15` |
| `exporter.readinessProbe.periodSeconds` | Period in seconds after which readiness probe will be repeated | int | `15` |
| `exporter.readinessProbe.successThreshold` | Success threshold for readiness probe of exporter | int | `2` |
| `exporter.readinessProbe.timeoutSeconds` | Timeout seconds for readiness probe of exporter | int | `3` |
| `exporter.resources` | cpu/memory resource limits/requests | object | `{}` |
| `exporter.scrapePath` | Exporter scrape path | string | `"/metrics"` |
| `exporter.script` | A custom custom Lua script that will be mounted to exporter for collection of custom metrics. Creates a ConfigMap and sets env var `REDIS_EXPORTER_SCRIPT`. | string | `""` |
| `exporter.serviceMonitor.disableAPICheck` | Disable API Check on ServiceMonitor | bool | `false` |
| `exporter.serviceMonitor.enabled` | When set true then use a ServiceMonitor to configure scraping | bool | `false` |
| `exporter.serviceMonitor.endpointAdditionalProperties` | Set additional properties for the ServiceMonitor endpoints such as relabeling, scrapeTimeout, tlsConfig, and more. | object | `{}` |
| `exporter.serviceMonitor.interval` | Set how frequently Prometheus should scrape (default is 30s) | string | `""` |
| `exporter.serviceMonitor.labels` | Set labels for the ServiceMonitor, use this to define your scrape label for Prometheus Operator | object | `{}` |
| `exporter.serviceMonitor.metricRelabelings` | | list | `[]` |
| `exporter.serviceMonitor.namespace` | Set the namespace the ServiceMonitor should be deployed | string | `.Release.Namespace` |
| `exporter.serviceMonitor.relabelings` | | list | `[]` |
| `exporter.serviceMonitor.telemetryPath` | Set path to redis-exporter telemtery-path (default is /metrics) | string | `""` |
| `exporter.serviceMonitor.timeout` | Set timeout for scrape (default is 10s) | string | `""` |
| `exporter.tag` | Exporter image tag | string | `"v1.86.0"` |
| `prometheusRule.additionalLabels` | Additional labels to be set in metadata. | object | `{}` |
| `prometheusRule.enabled` | If true, creates a Prometheus Operator PrometheusRule. | bool | `false` |
| `prometheusRule.interval` | How often rules in the group are evaluated (falls back to `global.evaluation_interval` if not set). | string | `"10s"` |
| `prometheusRule.namespace` | Namespace which Prometheus is running in. | string | `nil` |
| `prometheusRule.rules` | Rules spec template (see https://github.com/prometheus-operator/prometheus-operator/blob/master/Documentation/api.md#rule). | list | `[]` |
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
```bash
$ helm repo add dandydev https://dandydeveloper.github.io/charts
$ helm install \
--set image=redis \
--set tag=5.0.5-alpine \
dandydev/redis-ha
```
The above command sets the Redis server within `default` namespace.
Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,
```bash
helm install -f values.yaml dandydev/redis-ha
```
> **Tip**: You can use the default [values.yaml](values.yaml)
## Custom Redis and Sentinel config options
This chart allows for most redis or sentinel config options to be passed as a key value pair through the `values.yaml` under `redis.config` and `sentinel.config`. See links below for all available options.
[Example redis.conf](http://download.redis.io/redis-stable/redis.conf)
[Example sentinel.conf](http://download.redis.io/redis-stable/sentinel.conf)
For example `repl-timeout 60` would be added to the `redis.config` section of the `values.yaml` as:
```yml
repl-timeout: "60"
```
Note:
1. Some config options should be renamed by redis version,e.g.:
```yml
# In redis 5.x,see https://raw.githubusercontent.com/antirez/redis/5.0/redis.conf
min-replicas-to-write: 1
min-replicas-max-lag: 5
# In redis 4.x and redis 3.x,see https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf and https://raw.githubusercontent.com/antirez/redis/3.0/redis.conf
min-slaves-to-write 1
min-slaves-max-lag 5
```
Sentinel options supported must be in the the `sentinel <option> <master-group-name> <value>` format. For example, `sentinel down-after-milliseconds 30000` would be added to the `sentinel.config` section of the `values.yaml` as:
```yml
down-after-milliseconds: 30000
```
If more control is needed from either the redis or sentinel config then an entire config can be defined under `redis.customConfig` or `sentinel.customConfig`. Please note that these values will override any configuration options under their respective section. For example, if you define `sentinel.customConfig` then the `sentinel.config` is ignored.
## Host Kernel Settings
Redis may require some changes in the kernel of the host machine to work as expected, in particular increasing the `somaxconn` value and disabling transparent huge pages.
To do so, you can set up a privileged initContainer with the `sysctlImage` config values, for example:
```yml
sysctlImage:
enabled: true
mountHostSys: true
command:
- /bin/sh
- -xc
- |-
sysctl -w net.core.somaxconn=10000
echo never > /host-sys/kernel/mm/transparent_hugepage/enabled
```
## HAProxy startup
When HAProxy is enabled, it will attempt to connect to each announce-service of each redis replica instance in its init container before starting.
It will fail if announce-service IP is not available fast enough (10 seconds max by announce-service).
A such case could happen if the orchestator is pending the nomination of redis pods.
Risk is limited because announce-service is using `publishNotReadyAddresses: true`, although, in such case, HAProxy pod will be rescheduled afterward by the orchestrator.
PodDisruptionBudgets are not configured by default, you may need to set the `haproxy.podDisruptionBudget` parameter in values.yaml to enable it.
## Network policies
If `networkPolicy.enabled` is set to `true`, then a `NetworkPolicy` resource is created with default rules to allow inter-Redis and Sentinel connectivity.
This is a requirement for Redis Pods to come up successfully.
You will need to define `ingressRules` to permit your application connectivity to Redis.
The `selectors` block should be in the format of a [label selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors).
Templating is also supported in the selectors.
See such a configuration below.
```yaml
networkPolicy: true
ingressRules:
- selectors:
- namespaceSelector:
matchLabels:
name: my-redis-client-namespace
podSelector:
matchLabels:
# template example
app: |-
{{- .App.Name }}
## ports block is optional (defaults to below), define the block to override the defaults
# ports:
# - port: 6379
# protocol: TCP
# - port: 26379
# protocol: TCP
```
Should your Pod require additional egress rules, define them in a `egressRules` key which is structured identically to an `ingressRules` key.
## Sentinel and redis server split brain detection
Under not entirely known yet circumstances redis sentinel and its corresponding redis server reach a condition that this chart authors call "split brain" (for short). The observed behaviour is the following: the sentinel switches to the new re-elected master, but does not switch its redis server. Majority of original discussion on the problem has happened at the #121.
The proposed solution is currently implemented as a sidecar container that runs a bash script with the following logic:
1. At intervals defined by splitBrainDetection.interval, the sidecar checks which node is recognized as master by Sentinel.
2. If the current pod is the master according to Sentinel, it verifies that the local Redis server is also running as master.
3. If the current pod is not the master, it ensures the local Redis server is replicating from the correct master node.
4. If any of these checks fail, the sidecar will retry the check at intervals defined by splitBrainDetection.retryInterval.
5. If the checks continue to fail after the retry attempts, the sidecar triggers a reinitialization: it regenerates the Redis configuration and instructs the Redis server to shut down. Kubernetes will then automatically restart the container.
# Change Log
## 4.14.9 - ** POTENTIAL BREAKING CHANGE. **
Introduced the ability to change the Haproxy Deployment container pod
- Container port in redis-haproxy-deployment.yam has been changed. Was **redis.port** To **haproxy.containerPort**. Default value is 6379.
- Port in redis-haproxy-service.yaml has been changed. Was **redis.port** To **haproxy.servicePort**. Default value is 6379.
## 4.21.0 - BREAKING CHANGES (Kubernetes Deprecation)
This version introduced the deprecation of the PSP and subsequently added fields to the securityContexts that were introduced in Kubernetes v1.19:
https://kubernetes.io/docs/tutorials/security/seccomp/
As a result, from this version onwards Kubernetes versions older than 1.19 will fail to install without the removal of `.Values.containerSecurityContext.seccompProfile` and `.Values.haproxy.containerSecurityContext.seccompProfile` (If HAProxy is enabled)
----------------------------------------------
Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs)
@@ -1,25 +0,0 @@
Redis can be accessed via {{ if ne (int .Values.redis.port) 0 }}port {{ .Values.redis.port }}{{ end }} {{ if .Values.redis.tlsPort }} tls-port {{ .Values.redis.tlsPort }}{{ end }} and Sentinel can be accessed via {{ if ne (int .Values.sentinel.port) 0 }}port {{ .Values.sentinel.port }}{{ end }} {{ if .Values.sentinel.tlsPort }} tls-port {{ .Values.sentinel.tlsPort }}{{ end }} on the following DNS name from within your cluster:
{{ template "redis-ha.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
To connect to your Redis server:
{{- if .Values.auth }}
1. To retrieve the redis password:
echo $(kubectl get secret {{ template "redis-ha.fullname" . }} -o "jsonpath={.data['auth']}" | base64 --decode)
2. Connect to the Redis master pod that you can use as a client. By default the {{ template "redis-ha.fullname" . }}-server-0 pod is configured as the master:
kubectl exec -it {{ template "redis-ha.fullname" . }}-server-0 -n {{ .Release.Namespace }} -c redis -- sh
3. Connect using the Redis CLI (inside container):
redis-cli -a <REDIS-PASS-FROM-SECRET>
{{- else }}
1. Run a Redis pod that you can use as a client:
kubectl exec -it {{ template "redis-ha.fullname" . }}-server-0 -n {{ .Release.Namespace }} -c redis -- sh
2. Connect using the Redis CLI:
redis-cli -h {{ template "redis-ha.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local
{{- end }}
@@ -1,822 +0,0 @@
{{/* vim: set filetype=mustache: */}}
{{- define "config-redis.conf" }}
{{- if .Values.redis.customConfig }}
{{ tpl .Values.redis.customConfig . | indent 4 }}
{{- else }}
dir "/data"
port {{ .Values.redis.port }}
{{- if .Values.sentinel.tlsPort }}
tls-port {{ .Values.redis.tlsPort }}
tls-cert-file /tls-certs/{{ .Values.tls.certFile }}
tls-key-file /tls-certs/{{ .Values.tls.keyFile }}
{{- if .Values.tls.dhParamsFile }}
tls-dh-params-file /tls-certs/{{ .Values.tls.dhParamsFile }}
{{- end }}
{{- if .Values.tls.caCertFile }}
tls-ca-cert-file /tls-certs/{{ .Values.tls.caCertFile }}
{{- end }}
{{- if eq (default "yes" .Values.redis.authClients) "no"}}
tls-auth-clients no
{{- end }}
tls-replication {{ if .Values.redis.tlsReplication }}yes{{ else }}no{{ end }}
{{- end }}
{{- if .Values.redis.disableCommands }}
{{- range .Values.redis.disableCommands }}
rename-command {{ . }} ""
{{- end }}
{{- end }}
{{- range $key, $value := .Values.redis.config }}
{{- if kindIs "slice" $value }}
{{- range $value }}
{{ $key }} {{ . }}
{{- end }}
{{- else }}
{{ $key }} {{ $value }}
{{- end }}
{{- end }}
{{- if .Values.auth }}
requirepass replace-default-auth
masterauth replace-default-auth
{{- end }}
{{- end }}
{{- end }}
{{- define "config-sentinel.conf" }}
{{- if .Values.sentinel.customConfig }}
{{ tpl .Values.sentinel.customConfig . | indent 4 }}
{{- else }}
dir "/data"
port {{ .Values.sentinel.port }}
{{- if .Values.sentinel.bind }}
bind {{ .Values.sentinel.bind }}
{{- end }}
{{- if .Values.sentinel.tlsPort }}
tls-port {{ .Values.sentinel.tlsPort }}
tls-cert-file /tls-certs/{{ .Values.tls.certFile }}
tls-key-file /tls-certs/{{ .Values.tls.keyFile }}
{{- if .Values.tls.dhParamsFile }}
tls-dh-params-file /tls-certs/{{ .Values.tls.dhParamsFile }}
{{- end }}
{{- if .Values.tls.caCertFile }}
tls-ca-cert-file /tls-certs/{{ .Values.tls.caCertFile }}
{{- end }}
{{- if eq (default "yes" .Values.sentinel.authClients) "no"}}
tls-auth-clients no
{{- end }}
tls-replication {{ if .Values.sentinel.tlsReplication }}yes{{ else }}no{{ end }}
{{- end }}
{{- range $key, $value := .Values.sentinel.config }}
{{- if eq "maxclients" $key }}
{{ $key }} {{ $value }}
{{- else }}
sentinel {{ $key }} {{ template "redis-ha.masterGroupName" $ }} {{ $value }}
{{- end }}
{{- end }}
{{- if .Values.auth }}
sentinel auth-pass {{ template "redis-ha.masterGroupName" . }} replace-default-auth
{{- end }}
{{- if .Values.sentinel.auth }}
requirepass replace-default-sentinel-auth
{{- end }}
{{- if .Values.sentinel.resolveHostnames }}
sentinel resolve-hostnames yes
{{- end }}
{{- if .Values.sentinel.announceHostnames }}
sentinel announce-hostnames yes
{{- end }}
{{- end }}
{{- end }}
{{- define "lib.sh" }}
sentinel_get_master() {
set +e
if [ "$SENTINEL_PORT" -eq 0 ]; then
if [ "$RESOLVE_HOSTNAMES" = true ]; then
redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} sentinel get-master-addr-by-name "${MASTER_GROUP}" |\
head -n 1 | grep -E '^\s*[a-zA-Z0-9.-]+\s*$'
else
redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} sentinel get-master-addr-by-name "${MASTER_GROUP}" |\
head -n 1 | grep -E '((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?s*$))'
fi
else
if [ "$RESOLVE_HOSTNAMES" = true ]; then
redis-cli -h "${SERVICE}" -p "${SENTINEL_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} sentinel get-master-addr-by-name "${MASTER_GROUP}" |\
head -n 1 | grep -E '^\s*[a-zA-Z0-9.-]+\s*$'
else
redis-cli -h "${SERVICE}" -p "${SENTINEL_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} sentinel get-master-addr-by-name "${MASTER_GROUP}" |\
head -n 1 | grep -E '((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?s*$))'
fi
fi
set -e
}
sentinel_get_master_retry() {
master=''
retry=${1}
sleep=3
for i in $(seq 1 "${retry}"); do
master=$(sentinel_get_master)
if [ -n "${master}" ]; then
break
fi
sleep $((sleep + i))
done
echo "${master}"
}
identify_master() {
echo "Identifying redis master (get-master-addr-by-name).."
echo " using sentinel ({{ template "redis-ha.fullname" . }}), sentinel group name ({{ template "redis-ha.masterGroupName" . }})"
MASTER="$(sentinel_get_master_retry 3)"
if [ -n "${MASTER}" ]; then
echo " $(date) Found redis master (${MASTER})"
else
echo " $(date) Did not find redis master (${MASTER})"
fi
}
sentinel_update() {
echo "Updating sentinel config.."
echo " evaluating sentinel id (\${SENTINEL_ID_${INDEX}})"
eval MY_SENTINEL_ID="\$SENTINEL_ID_${INDEX}"
echo " sentinel id (${MY_SENTINEL_ID}), sentinel grp (${MASTER_GROUP}), quorum (${QUORUM})"
sed -i "1s/^/sentinel myid ${MY_SENTINEL_ID}\\n/" "${SENTINEL_CONF}"
if [ "$SENTINEL_TLS_REPLICATION_ENABLED" = true ]; then
echo " redis master (${1}:${REDIS_TLS_PORT})"
sed -i "2s/^/sentinel monitor ${MASTER_GROUP} ${1} ${REDIS_TLS_PORT} ${QUORUM} \\n/" "${SENTINEL_CONF}"
else
echo " redis master (${1}:${REDIS_PORT})"
sed -i "2s/^/sentinel monitor ${MASTER_GROUP} ${1} ${REDIS_PORT} ${QUORUM} \\n/" "${SENTINEL_CONF}"
fi
echo "sentinel announce-ip ${ANNOUNCE_IP}" >> ${SENTINEL_CONF}
if [ "$SENTINEL_PORT" -eq 0 ]; then
echo " announce (${ANNOUNCE_IP}:${SENTINEL_TLS_PORT})"
echo "sentinel announce-port ${SENTINEL_TLS_PORT}" >> ${SENTINEL_CONF}
else
echo " announce (${ANNOUNCE_IP}:${SENTINEL_PORT})"
echo "sentinel announce-port ${SENTINEL_PORT}" >> ${SENTINEL_CONF}
fi
}
redis_update() {
echo "Updating redis config.."
if [ "$REDIS_TLS_REPLICATION_ENABLED" = true ]; then
echo " we are slave of redis master (${1}:${REDIS_TLS_PORT})"
echo "slaveof ${1} ${REDIS_TLS_PORT}" >> "${REDIS_CONF}"
echo "slave-announce-port ${REDIS_TLS_PORT}" >> ${REDIS_CONF}
else
echo " we are slave of redis master (${1}:${REDIS_PORT})"
echo "slaveof ${1} ${REDIS_PORT}" >> "${REDIS_CONF}"
echo "slave-announce-port ${REDIS_PORT}" >> ${REDIS_CONF}
fi
echo "slave-announce-ip ${ANNOUNCE_IP}" >> ${REDIS_CONF}
}
copy_config() {
echo "Copying default redis config.."
echo " to '${REDIS_CONF}'"
cp /readonly-config/redis.conf "${REDIS_CONF}"
echo "Copying default sentinel config.."
echo " to '${SENTINEL_CONF}'"
cp /readonly-config/sentinel.conf "${SENTINEL_CONF}"
}
setup_defaults() {
echo "Setting up defaults.."
echo " using statefulset index (${INDEX})"
if [ "${INDEX}" = "0" ]; then
echo "Setting this pod as master for redis and sentinel.."
echo " using announce (${ANNOUNCE_IP})"
redis_update "${ANNOUNCE_IP}"
sentinel_update "${ANNOUNCE_IP}"
echo " make sure ${ANNOUNCE_IP} is not a slave (slaveof no one)"
sed -i "s/^.*slaveof.*//" "${REDIS_CONF}"
else
if [ "$RESOLVE_HOSTNAMES" = true ]; then
echo "Getting redis master hostname.."
echo " blindly assuming (${SERVICE}-announce-0.${NAMESPACE}.svc) is master"
DEFAULT_MASTER="${SERVICE}-announce-0.${NAMESPACE}.svc"
echo " identified redis (may be redis master) hostname (${DEFAULT_MASTER})"
else
echo "Getting redis master ip.."
echo " blindly assuming (${SERVICE}-announce-0) or (${SERVICE}-server-0) are master"
DEFAULT_MASTER="$(getent_hosts 0 | awk '{ print $1 }')"
if [ -z "${DEFAULT_MASTER}" ]; then
echo "Error: Unable to resolve redis master (getent hosts)."
exit 1
fi
echo " identified redis (may be redis master) ip (${DEFAULT_MASTER})"
fi
echo "Setting default slave config for redis and sentinel.."
echo " using master address (${DEFAULT_MASTER})"
redis_update "${DEFAULT_MASTER}"
sentinel_update "${DEFAULT_MASTER}"
fi
}
redis_ping() {
set +e
if [ "$REDIS_PORT" -eq 0 ]; then
redis-cli -h "${MASTER}"{{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} ping
else
redis-cli -h "${MASTER}"{{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}" ping
fi
set -e
}
redis_ping_retry() {
ping=''
retry=${1}
sleep=3
for i in $(seq 1 "${retry}"); do
if [ "$(redis_ping)" = "PONG" ]; then
ping='PONG'
break
fi
sleep $((sleep + i))
MASTER=$(sentinel_get_master)
done
echo "${ping}"
}
find_master() {
echo "Verifying redis master.."
if [ "$REDIS_PORT" -eq 0 ]; then
echo " ping (${MASTER}:${REDIS_TLS_PORT})"
else
echo " ping (${MASTER}:${REDIS_PORT})"
fi
if [ "$(redis_ping_retry 3)" != "PONG" ]; then
echo " $(date) Can't ping redis master (${MASTER})"
echo "Attempting to force failover (sentinel failover).."
if [ "$SENTINEL_PORT" -eq 0 ]; then
echo " on sentinel (${SERVICE}:${SENTINEL_TLS_PORT}), sentinel grp (${MASTER_GROUP})"
if redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} sentinel failover "${MASTER_GROUP}" | grep -q 'NOGOODSLAVE' ; then
echo " $(date) Failover returned with 'NOGOODSLAVE'"
echo "Setting defaults for this pod.."
setup_defaults
return 0
fi
else
echo " on sentinel (${SERVICE}:${SENTINEL_PORT}), sentinel grp (${MASTER_GROUP})"
if redis-cli -h "${SERVICE}" -p "${SENTINEL_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} sentinel failover "${MASTER_GROUP}" | grep -q 'NOGOODSLAVE' ; then
echo " $(date) Failover returned with 'NOGOODSLAVE'"
echo "Setting defaults for this pod.."
setup_defaults
return 0
fi
fi
echo "Hold on for 10sec"
sleep 10
echo "We should get redis master's ip now. Asking (get-master-addr-by-name).."
if [ "$SENTINEL_PORT" -eq 0 ]; then
echo " sentinel (${SERVICE}:${SENTINEL_TLS_PORT}), sentinel grp (${MASTER_GROUP})"
else
echo " sentinel (${SERVICE}:${SENTINEL_PORT}), sentinel grp (${MASTER_GROUP})"
fi
MASTER="$(sentinel_get_master)"
if [ "${MASTER}" ]; then
echo " $(date) Found redis master (${MASTER})"
echo "Updating redis and sentinel config.."
sentinel_update "${MASTER}"
redis_update "${MASTER}"
else
echo "$(date) Error: Could not failover, exiting..."
exit 1
fi
else
echo " $(date) Found reachable redis master (${MASTER})"
echo "Updating redis and sentinel config.."
sentinel_update "${MASTER}"
redis_update "${MASTER}"
fi
}
redis_ro_update() {
echo "Updating read-only redis config.."
echo " redis.conf set 'replica-priority 0'"
echo "replica-priority 0" >> ${REDIS_CONF}
}
getent_hosts() {
index=${1:-${INDEX}}
service="${SERVICE}-announce-${index}"
if [ "$RESOLVE_HOSTNAMES" = true ]; then
echo "${service}.${NAMESPACE}.svc"
else
host=$(getent hosts "${service}")
echo "${host}"
fi
}
identify_announce_ip() {
if [ "$ANNOUNCE_HOSTNAMES" = true ]; then
echo "Identify announce hostname for this pod.."
echo " using (${SERVICE}-announce-${INDEX}.${NAMESPACE}.svc)"
ANNOUNCE_IP="${SERVICE}-announce-${INDEX}.${NAMESPACE}.svc"
else
echo "Identify announce ip for this pod.."
echo " using (${SERVICE}-announce-${INDEX}) or (${SERVICE}-server-${INDEX})"
ANNOUNCE_IP=$(getent_hosts | awk '{ print $1 }')
fi
echo " identified announce (${ANNOUNCE_IP})"
}
{{- end }}
{{- define "vars.sh" }}
HOSTNAME="$(cat /proc/sys/kernel/hostname)"
{{- if .Values.ro_replicas }}
RO_REPLICAS="{{ .Values.ro_replicas }}"
{{- end }}
INDEX="${HOSTNAME##*-}"
SENTINEL_PORT={{ .Values.sentinel.port }}
ANNOUNCE_IP=''
MASTER=''
MASTER_GROUP="{{ template "redis-ha.masterGroupName" . }}"
QUORUM="{{ .Values.sentinel.quorum }}"
REDIS_CONF=/data/conf/redis.conf
REDIS_PORT={{ .Values.redis.port }}
REDIS_TLS_PORT={{ .Values.redis.tlsPort }}
SENTINEL_CONF=/data/conf/sentinel.conf
SENTINEL_TLS_PORT={{ .Values.sentinel.tlsPort }}
SERVICE={{ template "redis-ha.fullname" . }}
NAMESPACE="{{ .Release.Namespace }}"
SENTINEL_TLS_REPLICATION_ENABLED={{ default false .Values.sentinel.tlsReplication }}
REDIS_TLS_REPLICATION_ENABLED={{ default false .Values.redis.tlsReplication }}
RESOLVE_HOSTNAMES={{ default false .Values.sentinel.resolveHostnames }}
ANNOUNCE_HOSTNAMES={{ default false .Values.sentinel.announceHostnames }}
{{- end }}
{{- define "config-init.sh" }}
echo "$(date) Start..."
{{- include "vars.sh" . }}
set -eu
{{- include "lib.sh" . }}
mkdir -p /data/conf/
echo "Initializing config.."
copy_config
# where is redis master
identify_master
identify_announce_ip
if [ -z "${ANNOUNCE_IP}" ]; then
echo "Error: Could not resolve the announce address for this pod"
exit 1
elif [ "${MASTER}" ]; then
find_master
else
setup_defaults
fi
{{- if .Values.ro_replicas }}
# works only if index is less than 10
echo "Verifying redis read-only replica.."
echo " we have RO_REPLICAS='${RO_REPLICAS}' with INDEX='${INDEX}'"
if echo "${RO_REPLICAS}" | grep -q "${INDEX}" ; then
redis_ro_update
fi
{{- end }}
if [ "${AUTH:-}" ]; then
echo "Setting redis auth values.."
ESCAPED_AUTH=$(echo "${AUTH}" | sed -e 's/[\/&]/\\&/g');
sed -i "s/replace-default-auth/${ESCAPED_AUTH}/" "${REDIS_CONF}" "${SENTINEL_CONF}"
fi
if [ "${SENTINELAUTH:-}" ]; then
echo "Setting sentinel auth values"
ESCAPED_AUTH_SENTINEL=$(echo "$SENTINELAUTH" | sed -e 's/[\/&]/\\&/g');
sed -i "s/replace-default-sentinel-auth/${ESCAPED_AUTH_SENTINEL}/" "$SENTINEL_CONF"
fi
echo "$(date) Ready..."
{{- end }}
{{- define "trigger-failover-if-master.sh" }}
{{- if or (eq (int .Values.redis.port) 0) (eq (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
get_redis_role() {
is_master=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if (int .Values.redis.port) }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
info | grep -c 'role:master' || true
)
}
get_redis_role
if [[ "$is_master" -eq 1 ]]; then
echo "This node is currently master, we trigger a failover."
{{- $masterGroupName := include "redis-ha.masterGroupName" . }}
response=$(
redis-cli \
{{- if .Values.sentinel.auth }}
-a "${SENTINELAUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if (int .Values.sentinel.port) }}
-p {{ .Values.sentinel.port }} \
{{- else }}
-p {{ .Values.sentinel.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
SENTINEL failover {{ $masterGroupName }}
)
if [[ "$response" != "OK" ]] ; then
echo "$response"
exit 1
fi
timeout=30
while [[ "$is_master" -eq 1 && $timeout -gt 0 ]]; do
sleep 1
get_redis_role
timeout=$((timeout - 1))
done
echo "Failover successful"
fi
{{- end }}
{{- define "fix-split-brain.sh" }}
{{- include "vars.sh" . }}
ROLE=''
REDIS_MASTER=''
set -eu
{{- include "lib.sh" . }}
redis_role() {
set +e
if [ "$REDIS_PORT" -eq 0 ]; then
ROLE=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} info | grep role | sed 's/role://' | sed 's/\r//')
else
ROLE=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}" info | grep role | sed 's/role://' | sed 's/\r//')
fi
set -e
}
identify_redis_master() {
set +e
if [ "$REDIS_PORT" -eq 0 ]; then
REDIS_MASTER=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} info | grep master_host | sed 's/master_host://' | sed 's/\r//')
else
REDIS_MASTER=$(redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}" info | grep master_host | sed 's/master_host://' | sed 's/\r//')
fi
set -e
}
reinit() {
set +e
sh /readonly-config/init.sh
if [ "$REDIS_PORT" -eq 0 ]; then
echo "shutdown" | redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_TLS_PORT}" --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }}
else
echo "shutdown" | redis-cli {{ if .Values.auth }} -a "${AUTH}" --no-auth-warning{{ end }} -p "${REDIS_PORT}"
fi
set -e
}
identify_announce_ip
while [ -z "${ANNOUNCE_IP}" ]; do
echo "Error: Could not resolve the announce address for this pod."
sleep 30
identify_announce_ip
done
QUORUM_FAIL_COUNT=0
MAX_QUORUM_FAILURES=${MAX_QUORUM_FAILURES:-5}
trap "exit 0" TERM
while true; do
sleep {{ .Values.splitBrainDetection.interval }}
# where is redis master
identify_master
if [ "$MASTER" = "$ANNOUNCE_IP" ]; then
QUORUM_FAIL_COUNT=0
redis_role
if [ "$ROLE" != "master" ]; then
echo "waiting for redis to become master"
sleep {{ .Values.splitBrainDetection.retryInterval }}
identify_master
redis_role
echo "Redis role is $ROLE, expected role is master. No need to reinitialize."
if [ "$ROLE" != "master" ]; then
echo "Redis role is $ROLE, expected role is master, reinitializing"
reinit
fi
fi
elif [ "${MASTER}" ]; then
QUORUM_FAIL_COUNT=0
identify_redis_master
if [ "$REDIS_MASTER" != "$MASTER" ]; then
echo "Redis master and local master are not the same. waiting."
sleep {{ .Values.splitBrainDetection.retryInterval }}
identify_master
if [ "$MASTER" = "$ANNOUNCE_IP" ]; then
echo "This pod became master during wait, skipping reinit"
else
identify_redis_master
echo "Redis master is ${MASTER}, expected master is ${REDIS_MASTER}. No need to reinitialize."
if [ "${REDIS_MASTER}" != "${MASTER}" ]; then
echo "Redis master is ${MASTER}, expected master is ${REDIS_MASTER}, reinitializing"
reinit
fi
fi
fi
else
QUORUM_FAIL_COUNT=$((QUORUM_FAIL_COUNT + 1))
echo "WARNING: Sentinel returned no master (quorum may be broken). Failure count: $QUORUM_FAIL_COUNT/$MAX_QUORUM_FAILURES"
if [ "$QUORUM_FAIL_COUNT" -ge "$MAX_QUORUM_FAILURES" ]; then
echo "ERROR: Quorum broken for $MAX_QUORUM_FAILURES consecutive checks. Attempting sentinel reset..."
if [ "$SENTINEL_PORT" -eq 0 ]; then
redis-cli -h "${SERVICE}" -p "${SENTINEL_TLS_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} --tls --cacert /tls-certs/{{ .Values.tls.caCertFile }} {{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{ end }} sentinel reset "${MASTER_GROUP}" || true
else
redis-cli -h "${SERVICE}" -p "${SENTINEL_PORT}" {{ if .Values.sentinel.auth }} -a "${SENTINELAUTH}" --no-auth-warning{{ end }} sentinel reset "${MASTER_GROUP}" || true
fi
QUORUM_FAIL_COUNT=0
fi
fi
done
{{- end }}
{{- define "config-haproxy.cfg" }}
{{- if .Values.haproxy.customConfig }}
{{ tpl .Values.haproxy.customConfig . | indent 4 }}
{{- else }}
defaults REDIS
mode tcp
timeout connect {{ .Values.haproxy.timeout.connect }}
timeout server {{ .Values.haproxy.timeout.server }}
timeout client {{ .Values.haproxy.timeout.client }}
timeout check {{ .Values.haproxy.timeout.check }}
timeout tunnel {{ .Values.haproxy.timeout.tunnel }}
listen health_check_http_url
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:8888 {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
mode http
monitor-uri /healthz
option dontlognull
{{- $root := . }}
{{- $fullName := include "redis-ha.fullname" . }}
{{- $replicas := int (toString .Values.replicas) }}
{{- $masterGroupName := include "redis-ha.masterGroupName" . }}
{{- range $i := until $replicas }}
# Check Sentinel and whether they are nominated master
backend check_if_redis_is_master_{{ $i }}
mode tcp
option tcp-check
tcp-check connect
{{- if $root.Values.sentinel.auth }}
tcp-check send "AUTH ${SENTINELAUTH}"\r\n
tcp-check expect string +OK
{{- end }}
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send SENTINEL\ get-master-addr-by-name\ {{ $masterGroupName }}\r\n
tcp-check expect string REPLACE_ANNOUNCE{{ $i }}
tcp-check send QUIT\r\n
{{- range $i := until $replicas }}
{{- if $.Values.sentinel.resolveHostnames }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}.{{ $.Release.Namespace }}.svc:26379 check inter {{ $root.Values.haproxy.checkInterval }}
{{- else }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}:26379 check inter {{ $root.Values.haproxy.checkInterval }}
{{- end }}
{{- end }}
{{- end }}
# decide redis backend to use
#master
frontend ft_redis_master
{{- if .Values.haproxy.tls.enabled }}
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ $root.Values.haproxy.containerPort }} ssl crt {{ .Values.haproxy.tls.certMountPath }}{{ .Values.haproxy.tls.keyName }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
{{ else }}
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ if ne (int $root.Values.redis.port) 0 }}{{ $root.Values.redis.port }}{{ else }}{{ $root.Values.redis.tlsPort }}{{ end }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
{{- end }}
use_backend bk_redis_master
{{- if .Values.haproxy.readOnly.enabled }}
#slave
frontend ft_redis_slave
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ .Values.haproxy.readOnly.port }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
use_backend bk_redis_slave
{{- end }}
# Check all redis servers to see if they think they are master
backend bk_redis_master
{{- if .Values.haproxy.stickyBalancing }}
balance source
hash-type consistent
{{- end }}
mode tcp
option tcp-check
tcp-check connect
{{- if .Values.auth }}
tcp-check send "AUTH ${AUTH}"\r\n
tcp-check expect string +OK
{{- end }}
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
{{- range $i := until $replicas }}
use-server R{{ $i }} if { srv_is_up(R{{ $i }}) } { nbsrv(check_if_redis_is_master_{{ $i }}) ge 2 }
{{- if $.Values.sentinel.resolveHostnames }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}.{{ $.Release.Namespace }}.svc:{{ $root.Values.redis.port }} check inter {{ $root.Values.haproxy.checkInterval }} fall {{ $root.Values.haproxy.checkFall }} rise 1
{{- else }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}:{{ $root.Values.redis.port }} check inter {{ $root.Values.haproxy.checkInterval }} fall {{ $root.Values.haproxy.checkFall }} rise 1
{{- end }}
{{- end }}
{{- if .Values.haproxy.readOnly.enabled }}
backend bk_redis_slave
{{- if .Values.haproxy.stickyBalancing }}
balance source
hash-type consistent
{{- end }}
mode tcp
option tcp-check
tcp-check connect
{{- if .Values.auth }}
tcp-check send "AUTH ${AUTH}"\r\n
tcp-check expect string +OK
{{- end }}
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send info\ replication\r\n
tcp-check expect string role:slave
tcp-check send QUIT\r\n
tcp-check expect string +OK
{{- range $i := until $replicas }}
{{- if $.Values.sentinel.resolveHostnames }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}.{{ $.Release.Namespace }}.svc:{{ $root.Values.redis.port }} check inter {{ $root.Values.haproxy.checkInterval }} fall {{ $root.Values.haproxy.checkFall }} rise 1
{{- else }}
server R{{ $i }} {{ $fullName }}-announce-{{ $i }}:{{ $root.Values.redis.port }} check inter {{ $root.Values.haproxy.checkInterval }} fall {{ $root.Values.haproxy.checkFall }} rise 1
{{- end }}
{{- end }}
{{- end }}
{{- if .Values.haproxy.metrics.enabled }}
frontend stats
mode http
bind {{ if .Values.haproxy.IPv6.enabled }}[::]{{ end }}:{{ .Values.haproxy.metrics.port }} {{ if .Values.haproxy.IPv6.enabled }}v4v6{{ end }}
http-request use-service prometheus-exporter if { path {{ .Values.haproxy.metrics.scrapePath }} }
stats enable
stats uri /stats
stats refresh 10s
{{- end }}
{{- if .Values.haproxy.extraConfig }}
# Additional configuration
{{ .Values.haproxy.extraConfig | indent 4 }}
{{- end }}
{{- end }}
{{- end }}
{{- define "config-haproxy_init.sh" }}
HAPROXY_CONF=/data/haproxy.cfg
cp /readonly/haproxy.cfg "$HAPROXY_CONF"
{{- $fullName := include "redis-ha.fullname" . }}
{{- $replicas := int (toString .Values.replicas) }}
{{- $resolveHostnames := .Values.sentinel.resolveHostnames }}
{{- $namespace := .Release.Namespace }}
{{- range $i := until $replicas }}
{{- if $resolveHostnames }}
ANNOUNCE_IP{{ $i }}="{{ $fullName }}-announce-{{ $i }}.{{ $namespace }}.svc"
echo "Using hostname for {{ $fullName }}-announce-{{ $i }}.{{ $namespace }}.svc: $ANNOUNCE_IP{{ $i }}"
{{- else }}
for loop in $(seq 1 10); do
getent hosts {{ $fullName }}-announce-{{ $i }} && break
echo "Waiting for service {{ $fullName }}-announce-{{ $i }} to be ready ($loop) ..." && sleep 1
done
ANNOUNCE_IP{{ $i }}=$(getent hosts "{{ $fullName }}-announce-{{ $i }}" | awk '{ print $1 }')
if [ -z "$ANNOUNCE_IP{{ $i }}" ]; then
echo "Could not resolve the announce address for {{ $fullName }}-announce-{{ $i }}"
exit 1
fi
{{- end }}
sed -i "s/REPLACE_ANNOUNCE{{ $i }}/$ANNOUNCE_IP{{ $i }}/" "$HAPROXY_CONF"
{{- end }}
{{- end }}
{{- define "redis_liveness.sh" }}
{{- if not (ne (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
response=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.redis.port) 0 }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
ping
)
echo "response=$response"
case $response in
PONG|LOADING*) ;;
*) exit 1 ;;
esac
exit 0
{{- end }}
{{- define "redis_readiness.sh" }}
{{- if not (ne (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
response=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.redis.port) 0 }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
ping
)
if [ "$response" != "PONG" ] ; then
echo "ping=$response"
exit 1
fi
response=$(
redis-cli \
{{- if .Values.auth }}
-a "${AUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.redis.port) 0 }}
-p {{ .Values.redis.port }} \
{{- else }}
-p {{ .Values.redis.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
role
)
role=$( echo "$response" | sed "1!d" )
if [ "$role" = "master" ]; then
echo "role=$role"
exit 0
elif [ "$role" = "slave" ]; then
repl=$( echo "$response" | sed "4!d" )
echo "role=$role; repl=$repl"
if [ "$repl" = "connected" ]; then
exit 0
else
exit 1
fi
else
echo "role=$role"
exit 1
fi
{{- end }}
{{- define "sentinel_liveness.sh" }}
{{- if not (ne (int .Values.sentinel.port) 0) }}
TLS_CLIENT_OPTION="--tls --cacert /tls-certs/{{ .Values.tls.caCertFile }}{{ if ne (default "yes" .Values.sentinel.authClients) "no"}} --cert /tls-certs/{{ .Values.tls.certFile }} --key /tls-certs/{{ .Values.tls.keyFile }}{{end}}"
{{- end }}
response=$(
redis-cli \
{{- if .Values.sentinel.auth }}
-a "${SENTINELAUTH}" --no-auth-warning \
{{- end }}
-h localhost \
{{- if ne (int .Values.sentinel.port) 0 }}
-p {{ .Values.sentinel.port }} \
{{- else }}
-p {{ .Values.sentinel.tlsPort }} ${TLS_CLIENT_OPTION} \
{{- end}}
ping
)
if [ "$response" != "PONG" ]; then
echo "$response"
exit 1
fi
echo "response=$response"
{{- end }}
@@ -1,154 +0,0 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "redis-ha.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "redis-ha.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Return sysctl image
*/}}
{{- define "redis.sysctl.image" -}}
{{- $registryName := default "docker.io" .Values.sysctlImage.registry -}}
{{- $tag := default "latest" .Values.sysctlImage.tag | toString -}}
{{- printf "%s/%s:%s" $registryName .Values.sysctlImage.repository $tag -}}
{{- end -}}
{{- /*
Credit: @technosophos
https://github.com/technosophos/common-chart/
labels.standard prints the standard Helm labels.
The standard labels are frequently used in metadata.
*/ -}}
{{- define "labels.standard" -}}
app: {{ template "redis-ha.name" . }}
heritage: {{ .Release.Service | quote }}
release: {{ .Release.Name | quote }}
chart: {{ template "chartref" . }}
{{- end -}}
{{- /*
Credit: @technosophos
https://github.com/technosophos/common-chart/
chartref prints a chart name and version.
It does minimal escaping for use in Kubernetes labels.
Example output:
zookeeper-1.2.3
wordpress-3.2.1_20170219
*/ -}}
{{- define "chartref" -}}
{{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "redis-ha.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "redis-ha.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{- define "redis-ha.masterGroupName" -}}
{{- $masterGroupName := tpl ( .Values.redis.masterGroupName | default "") . -}}
{{- $validMasterGroupName := regexMatch "^[\\w-\\.]+$" $masterGroupName -}}
{{- if $validMasterGroupName -}}
{{ $masterGroupName }}
{{- else -}}
{{ required "A valid .Values.redis.masterGroupName entry is required (matching ^[\\w-\\.]+$)" ""}}
{{- end -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for poddisruptionbudget.
*/}}
{{- define "redis-ha.podDisruptionBudget.apiVersion" -}}
{{- if .Capabilities.APIVersions.Has "policy/v1" }}
{{- print "policy/v1" -}}
{{- else -}}
{{- print "policy/v1beta1" -}}
{{- end -}}
{{- end -}}
{{/*
Return true if the detected platform is Openshift
Usage:
{{- include "common.compatibility.isOpenshift" . -}}
*/}}
{{- define "compatibility.isOpenshift" -}}
{{- if .Capabilities.APIVersions.Has "security.openshift.io/v1" -}}
{{- true -}}
{{- end -}}
{{- end -}}
{{/*
Render a compatible securityContext depending on the platform. By default it is maintained as it is. In other platforms like Openshift we remove default user/group values that do not work out of the box with the restricted-v1 SCC
Usage:
{{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) -}}
*/}}
{{- define "compatibility.renderSecurityContext" -}}
{{- $adaptedContext := .secContext -}}
{{- if (((.context.Values.global).compatibility).openshift) -}}
{{- if or (eq .context.Values.global.compatibility.openshift.adaptSecurityContext "force") (and (eq .context.Values.global.compatibility.openshift.adaptSecurityContext "auto") (include "compatibility.isOpenshift" .context)) -}}
{{/* Remove incompatible user/group values that do not work in Openshift out of the box */}}
{{- $adaptedContext = omit $adaptedContext "fsGroup" "runAsUser" "runAsGroup" -}}
{{- if not .secContext.seLinuxOptions -}}
{{/* If it is an empty object, we remove it from the resulting context because it causes validation issues */}}
{{- $adaptedContext = omit $adaptedContext "seLinuxOptions" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/* Remove fields that are disregarded when running the container in privileged mode */}}
{{- if $adaptedContext.privileged -}}
{{- $adaptedContext = omit $adaptedContext "capabilities" "seLinuxOptions" -}}
{{- end -}}
{{- omit $adaptedContext "enabled" | toYaml -}}
{{- end -}}
{{/*
Defines the redis ports to be used inside network policies
Usage:
{{- include "redis-ports" . -}}
*/}}
{{- define "redis-ports" -}}
{{- if ne (int .Values.redis.port) 0 }}
- port: {{ .Values.redis.port }}
protocol: TCP
{{- end -}}
{{- if ne (int .Values.sentinel.port) 0 }}
- port: {{ .Values.sentinel.port }}
protocol: TCP
{{- end -}}
{{- if ne (int .Values.redis.tlsPort) 0 }}
- port: {{ .Values.redis.tlsPort }}
protocol: TCP
{{- end -}}
{{- if ne (int .Values.sentinel.tlsPort) 0 }}
- port: {{ .Values.sentinel.tlsPort }}
protocol: TCP
{{- end -}}
{{- end -}}
@@ -1,19 +0,0 @@
{{- if and .Values.auth (not (tpl (.Values.existingSecret | default "" ) . )) -}}
{{ $fullAnnotations := mustMerge .Values.redis.annotations .Values.authSecretAnnotations }}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
{{- with $fullAnnotations }}
annotations: {{ . | toYaml | nindent 4 }}
{{- end }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{ .Values.authKey }}: {{ .Values.redisPassword | b64enc | quote }}
{{- end -}}
@@ -1,64 +0,0 @@
{{- $fullName := include "redis-ha.fullname" . }}
{{- $namespace := .Release.Namespace -}}
{{- $replicas := int (toString .Values.replicas) }}
{{- $root := . }}
{{- range $i := until $replicas }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $fullName }}-announce-{{ $i }}
namespace: {{ $namespace | quote }}
labels:
{{ include "labels.standard" $root | indent 4 }}
{{- range $key, $value := $root.Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if (semverCompare "<=1.10-0" $.Capabilities.KubeVersion.Version) }}
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
{{- end }}
{{- if $root.Values.serviceAnnotations }}
{{ toYaml $root.Values.serviceAnnotations | indent 4 }}
{{- end }}
spec:
{{- if (semverCompare ">=1.11-0" $.Capabilities.KubeVersion.Version) }}
publishNotReadyAddresses: true
{{- end }}
type: ClusterIP
ports:
{{- if ne (int $root.Values.redis.port) 0 }}
- name: tcp-server
port: {{ $root.Values.redis.port }}
protocol: TCP
targetPort: redis
{{- end }}
{{- if $root.Values.redis.tlsPort }}
- name: server-tls
port: {{ $root.Values.redis.tlsPort }}
protocol: TCP
targetPort: redis-tls
{{- end }}
{{- if ne (int $root.Values.sentinel.port) 0 }}
- name: tcp-sentinel
port: {{ $root.Values.sentinel.port }}
protocol: TCP
targetPort: sentinel
{{- end }}
{{- if $root.Values.sentinel.tlsPort }}
- name: sentinel-tls
port: {{ $root.Values.sentinel.tlsPort }}
protocol: TCP
targetPort: sentinel-tls
{{- end }}
{{- if $root.Values.exporter.enabled }}
- name: http-exporter
port: {{ $root.Values.exporter.port }}
protocol: TCP
targetPort: {{ $root.Values.exporter.portName }}
{{- end }}
selector:
release: {{ $root.Release.Name }}
app: {{ include "redis-ha.name" $root }}
"statefulset.kubernetes.io/pod-name": {{ $fullName }}-server-{{ $i }}
{{- end }}
@@ -1,37 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "redis-ha.fullname" . }}-configmap
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.configmap.labels }}
{{ $key }}: {{ $value | toString }}
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
data:
redis.conf: |
{{- include "config-redis.conf" . }}
sentinel.conf: |
{{- include "config-sentinel.conf" . }}
init.sh: |
{{- include "config-init.sh" . }}
fix-split-brain.sh: |
{{- include "fix-split-brain.sh" . }}
{{ if .Values.haproxy.enabled }}
haproxy.cfg: |
{{- include "config-haproxy.cfg" . }}
{{- end }}
haproxy_init.sh: |
{{- include "config-haproxy_init.sh" . }}
trigger-failover-if-master.sh: |
{{- include "trigger-failover-if-master.sh" . }}
@@ -1,14 +0,0 @@
{{- if .Values.exporter.script }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "redis-ha.fullname" . }}-exporter-script-configmap
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
data:
script: {{ toYaml .Values.exporter.script | indent 2 }}
{{- end }}
@@ -1,20 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "redis-ha.fullname" . }}-health-configmap
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
data:
redis_liveness.sh: |
{{- include "redis_liveness.sh" . }}
redis_readiness.sh: |
{{- include "redis_readiness.sh" . }}
sentinel_liveness.sh: |
{{- include "sentinel_liveness.sh" . }}
@@ -1,68 +0,0 @@
{{- if .Values.networkPolicy.enabled }}
{{- $root := . }}
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: {{ template "redis-ha.fullname" . }}-network-policy
namespace: {{ .Release.Namespace | quote }}
{{- if .Values.networkPolicy.annotations }}
annotations:
{{- range $key, $value := .Values.networkPolicy.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.networkPolicy.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
policyTypes:
- Ingress
- Egress
egress:
- to:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
{{- include "redis-ports" . | nindent 6 }}
{{- range $rule := .Values.networkPolicy.egressRules }}
- to:
{{ (tpl (toYaml $rule.selectors) $) | indent 7 }}
ports:
{{ toYaml $rule.ports | indent 7 }}
{{- end }}
ingress:
- from:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
{{- include "redis-ports" . | nindent 6 }}
{{- if .Values.haproxy.enabled }}
- from:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
ports:
{{- include "redis-ports" . | nindent 6 }}
{{- end }}
{{- range $rule := .Values.networkPolicy.ingressRules }}
- from:
{{ (tpl (toYaml $rule.selectors) $) | indent 7 }}
ports:
{{- if $rule.ports }}
{{ toYaml $rule.ports | indent 7 }}
{{- else }}
{{- include "redis-ports" $root | nindent 6 }}
{{- end }}
{{- end }}
{{- end }}
@@ -1,18 +0,0 @@
{{- if .Values.podDisruptionBudget -}}
apiVersion: {{ template "redis-ha.podDisruptionBudget.apiVersion" . }}
kind: PodDisruptionBudget
metadata:
name: {{ template "redis-ha.fullname" . }}-pdb
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
selector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
{{ toYaml .Values.podDisruptionBudget | indent 2 }}
{{- end -}}
@@ -1,19 +0,0 @@
{{- if .Values.prometheusRule.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: {{ template "redis-ha.fullname" . }}
{{- if .Values.prometheusRule.namespace }}
namespace: {{ .Values.prometheusRule.namespace }}
{{- end }}
{{- if .Values.prometheusRule.additionalLabels }}
labels: {{- toYaml .Values.prometheusRule.additionalLabels | nindent 4 }}
{{- end }}
spec:
groups:
- name: {{ template "redis-ha.fullname" . }}
{{- if .Values.prometheusRule.interval }}
interval: {{ .Values.prometheusRule.interval }}
{{- end }}
rules: {{- tpl (toYaml .Values.prometheusRule.rules) . | nindent 8 }}
{{- end }}
@@ -1,19 +0,0 @@
{{- if and .Values.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
rules:
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
{{- end }}
@@ -1,19 +0,0 @@
{{- if and .Values.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
subjects:
- kind: ServiceAccount
name: {{ template "redis-ha.serviceAccountName" . }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ template "redis-ha.fullname" . }}
{{- end }}
@@ -1,32 +0,0 @@
{{- if not .Values.restore.existingSecret }}
{{- $regexRestoreS3 := "^s3://.+|^S3://.+" -}}
{{- $regexRestoreSSH := "^.+@.+:.+" -}}
{{- if or (regexFind $regexRestoreSSH (toString .Values.restore.ssh.source)) (regexFind $regexRestoreS3 (toString .Values.restore.s3.source)) }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "redis-ha.fullname" . }}-secret
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{- if regexFind $regexRestoreSSH (toString .Values.restore.ssh.source) }}
SSH_KEY: "{{ .Values.restore.ssh.key | b64enc }}"
{{- end }}
{{- if regexFind $regexRestoreS3 (toString .Values.restore.s3.source) }}
AWS_SECRET_ACCESS_KEY: "{{ .Values.restore.s3.secret_key | b64enc }}"
AWS_ACCESS_KEY_ID: "{{ .Values.restore.s3.access_key | b64enc }}"
{{- end }}
{{- end }}
{{- end }}
@@ -1,57 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- if .Values.exporter.enabled }}
exporter: enabled
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.serviceLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if .Values.serviceAnnotations }}
{{ toYaml .Values.serviceAnnotations | indent 4 }}
{{- end }}
spec:
type: ClusterIP
clusterIP: None
ports:
{{- if ne (int .Values.redis.port) 0 }}
- name: tcp-server
port: {{ .Values.redis.port }}
protocol: TCP
targetPort: redis
{{- end }}
{{- if .Values.redis.tlsPort }}
- name: server-tls
port: {{ .Values.redis.tlsPort }}
protocol: TCP
targetPort: redis-tls
{{- end }}
{{- if ne (int .Values.sentinel.port) 0 }}
- name: tcp-sentinel
port: {{ .Values.sentinel.port }}
protocol: TCP
targetPort: sentinel
{{- end }}
{{- if .Values.sentinel.tlsPort }}
- name: sentinel-tls
port: {{ .Values.sentinel.tlsPort }}
protocol: TCP
targetPort: sentinel-tls
{{- end }}
{{- if .Values.exporter.enabled }}
- name: http-exporter-port
port: {{ .Values.exporter.port }}
protocol: TCP
targetPort: {{ .Values.exporter.portName }}
{{- end }}
selector:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
@@ -1,35 +0,0 @@
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "redis-ha.serviceAccountName" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- if .Values.serviceAccount.annotations }}
annotations:
{{ toYaml .Values.serviceAccount.annotations | indent 4 }}
{{- end }}
{{- if or .Values.auth .Values.sentinel.auth }}
secrets:
{{- end }}
{{- if and (and (.Values.auth) (.Values.sentinel.auth)) (eq (.Values.existingSecret) (.Values.sentinel.existingSecret)) }}
- name: {{ default (include "redis-ha.fullname" .) (tpl (.Values.existingSecret | default "" ) . ) }}
{{- else }}
{{- if .Values.auth }}
- name: {{ default (include "redis-ha.fullname" .) (tpl (.Values.existingSecret | default "" ) . ) }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: {{ default (printf "%s-sentinel" (include "redis-ha.fullname" .)) (tpl (.Values.sentinel.existingSecret | default "" ) . ) }}
{{- end }}
{{- end }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 0 }}
{{- end }}
{{- end }}
@@ -1,47 +0,0 @@
{{- if and ( or .Values.exporter.serviceMonitor.disableAPICheck ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ) ( .Values.exporter.serviceMonitor.enabled ) ( .Values.exporter.enabled ) }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ template "redis-ha.fullname" . }}
namespace: {{ .Values.exporter.serviceMonitor.namespace | default .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.exporter.serviceMonitor.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
endpoints:
- targetPort: {{ .Values.exporter.port }}
{{- if .Values.exporter.serviceMonitor.interval }}
interval: {{ .Values.exporter.serviceMonitor.interval }}
{{- end }}
{{- if .Values.exporter.serviceMonitor.telemetryPath }}
path: {{ .Values.exporter.serviceMonitor.telemetryPath }}
{{- end }}
{{- if .Values.exporter.serviceMonitor.timeout }}
scrapeTimeout: {{ .Values.exporter.serviceMonitor.timeout }}
{{- end }}
{{- with .Values.exporter.serviceMonitor.endpointAdditionalProperties }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if .Values.exporter.serviceMonitor.metricRelabelings }}
metricRelabelings:
{{- toYaml .Values.exporter.serviceMonitor.metricRelabelings | nindent 6 }}
{{- end }}
{{- if .Values.exporter.serviceMonitor.relabelings }}
relabelings:
{{- toYaml .Values.exporter.serviceMonitor.relabelings | nindent 6 }}
{{- end }}
jobLabel: {{ template "redis-ha.fullname" . }}
namespaceSelector:
matchNames:
- {{ .Release.Namespace | quote }}
selector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
exporter: enabled
{{- end }}
@@ -1,668 +0,0 @@
{{- $regexRestoreS3 := "^s3://.+|^S3://.+" -}}
{{- $regexRestoreSSH := "^.+@.+:.+" -}}
{{- $regexRestoreRedis := "^redis://(?:[A-Za-z0-9_]+(?::[^@]+)?@)?[A-Za-z0-9.-]+(?::\\d{1,5})?(?:/\\d+)?$" -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ template "redis-ha.fullname" . }}-server
namespace: {{ .Release.Namespace | quote }}
labels:
{{ template "redis-ha.fullname" . }}: replica
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{ include "labels.standard" . | indent 4 }}
annotations:
{{ toYaml .Values.redis.annotations | indent 4 }}
spec:
selector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
serviceName: {{ template "redis-ha.fullname" . }}
replicas: {{ .Values.replicas }}
podManagementPolicy: {{ .Values.podManagementPolicy }}
updateStrategy:
type: {{ .Values.redis.updateStrategy.type }}
{{- if .Values.redis.minReadySeconds }}
minReadySeconds: {{ .Values.redis.minReadySeconds }}
{{- end }}
template:
metadata:
annotations:
checksum/init-config: {{ print (include "config-redis.conf" .) (include "config-sentinel.conf" .) (include "config-init.sh" .) (include "fix-split-brain.sh" .) (include "redis_liveness.sh" .) (include "redis_readiness.sh" .) (include "sentinel_liveness.sh" .) (include "trigger-failover-if-master.sh" .)| sha256sum }}
{{- if .Values.redis.podAnnotations }}
{{ toYaml .Values.redis.podAnnotations | indent 8 }}
{{- end }}
{{- if and (.Values.exporter.enabled) (not .Values.exporter.serviceMonitor.enabled) }}
prometheus.io/port: "{{ .Values.exporter.port }}"
prometheus.io/scrape: "true"
prometheus.io/path: {{ .Values.exporter.scrapePath }}
{{- end }}
labels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
{{ template "redis-ha.fullname" . }}: replica
{{- range $key, $value := .Values.labels }}
{{ $key }}: {{ $value | toString }}
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
{{- if .Values.redis.terminationGracePeriodSeconds }}
terminationGracePeriodSeconds: {{ .Values.redis.terminationGracePeriodSeconds }}
{{- end }}
{{- if .Values.schedulerName }}
schedulerName: "{{ .Values.schedulerName }}"
{{- end }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
{{- if .Values.tolerations }}
tolerations:
{{ toYaml .Values.tolerations | indent 8 }}
{{- end }}
affinity:
{{- if .Values.affinity }}
{{- with .Values.affinity }}
{{ tpl . $ | indent 8 }}
{{- end }}
{{- else }}
{{- if .Values.additionalAffinities }}
{{ toYaml .Values.additionalAffinities | indent 8 }}
{{- end }}
podAntiAffinity:
{{- if .Values.hardAntiAffinity }}
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
{{ template "redis-ha.fullname" . }}: replica
topologyKey: kubernetes.io/hostname
{{- else }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
{{ template "redis-ha.fullname" . }}: replica
topologyKey: kubernetes.io/hostname
{{- end }}
{{- end }}
{{- if .Values.topologySpreadConstraints.enabled }}
topologySpreadConstraints:
- maxSkew: {{ .Values.topologySpreadConstraints.maxSkew | default 1 }}
topologyKey: {{ .Values.topologySpreadConstraints.topologyKey | default "topology.kubernetes.io/zone" }}
whenUnsatisfiable: {{ .Values.topologySpreadConstraints.whenUnsatisfiable | default "ScheduleAnyway" }}
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
{{ template "redis-ha.fullname" . }}: replica
{{- end }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }}
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.securityContext "context" $) | nindent 8 }}
serviceAccountName: {{ template "redis-ha.serviceAccountName" . }}
automountServiceAccountToken: {{ .Values.serviceAccount.automountToken }}
initContainers:
{{- if .Values.sysctlImage.enabled }}
- name: init-sysctl
image: {{ template "redis.sysctl.image" . }}
imagePullPolicy: {{ .Values.sysctlImage.pullPolicy }}
resources: {{ toYaml .Values.sysctlImage.resources | nindent 10 }}
{{- if .Values.sysctlImage.mountHostSys }}
volumeMounts:
- name: host-sys
mountPath: /host-sys
{{- end }}
command: {{ toYaml .Values.sysctlImage.command | nindent 10 }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
{{- end }}
{{- if and .Values.hostPath.path .Values.hostPath.chown }}
- name: hostpath-chown
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
securityContext:
runAsUser: 0
runAsNonRoot: false
resources: {{ toYaml .Values.init.resources | nindent 10 }}
command:
- chown
- "{{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }}"
- /data
volumeMounts:
- name: data
mountPath: /data
{{- end }}
- name: config-init
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources: {{ toYaml .Values.init.resources | nindent 10 }}
command:
- sh
args:
- /readonly-config/init.sh
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
env:
{{- $replicas := int (toString .Values.replicas) -}}
{{- range $i := until $replicas }}
- name: SENTINEL_ID_{{ $i }}
value: {{ printf "%s\n%s\nindex: %d" (include "redis-ha.name" $) ($.Release.Name) $i | sha256sum | trunc 40 }}
{{- end }}
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.existingSecret | default "" ) . }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.sentinel.existingSecret | default "" ) . }}
name: {{ tpl (.Values.sentinel.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
- name: data
mountPath: /data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end}}
{{ if regexFind $regexRestoreS3 (toString .Values.restore.s3.source) }}
- name: restore-s3
image: s3cmd/s3cmd:latest
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources: {{ toYaml .Values.init.resources | nindent 10 }}
command:
- sh
args:
- "-c"
- "timeout -t {{ .Values.restore.timeout }} \
s3cmd get {{ if .Values.restore.s3.region }}--region {{ .Values.restore.s3.region }} {{ end }}--force '{{ .Values.restore.s3.source }}' /data/dump.rdb_ \
&& test -s /data/dump.rdb_ \
&& if test -s /data/dump.rdb; \
then cp -v /data/dump.rdb /data/dump.rdb_orig; fi \
&& mv -v /data/dump.rdb_ /data/dump.rdb"
envFrom:
- secretRef:
{{- if .Values.restore.existingSecret }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ include "redis-ha.fullname" . }}-secret
{{- end }}
volumeMounts:
- name: data
mountPath: /data
{{- end }}
{{ if regexFind $regexRestoreSSH (toString .Values.restore.ssh.source) }}
- name: restore-ssh
image: lgatica/openssh-client:latest
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources: {{ toYaml .Values.init.resources | nindent 10 }}
command:
- sh
args:
- "-c"
- "rm -f key && echo -e \"${SSH_KEY}\" >key \
&& chmod 400 key \
&& timeout {{ .Values.restore.timeout }} \
scp -i key \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
'{{ .Values.restore.ssh.source }}' \
/data/dump.rdb_ \
&& test -s /data/dump.rdb_ \
&& if test -s /data/dump.rdb; \
then cp -v /data/dump.rdb /data/dump.rdb_orig; fi \
&& mv -v /data/dump.rdb_ /data/dump.rdb"
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
envFrom:
- secretRef:
{{- if .Values.restore.existingSecret }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ include "redis-ha.fullname" . }}-secret
{{- end }}
volumeMounts:
- name: data
mountPath: /data
{{- end }}
{{ if regexFind $regexRestoreRedis (toString .Values.restore.redis.source) }}
- name: restore-redis
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources: {{ toYaml .Values.init.resources | nindent 10 }}
command:
- sh
args:
- "-c"
- "echo $HOSTNAME | grep -q 'ha-server-0' \
&& nc -w 5 -vz {{ regexReplaceAll "^redis:\\/\\/(.*)" .Values.restore.redis.source "${1}" }} \
&& test ! -s /data/dump.rdb \
&& timeout {{ .Values.restore.timeout }} \
redis-cli -u {{ .Values.restore.redis.source }} --rdb /data/dump.rdb_ \
&& test -s /data/dump.rdb_ \
&& if test -s /data/dump.rdb; \
then cp -v /data/dump.rdb /data/dump.rdb_orig; fi \
&& mv -v /data/dump.rdb_ /data/dump.rdb || true"
{{- if .Values.restore.existingSecret }}
envFrom:
- secretRef:
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- end }}
volumeMounts:
- name: data
mountPath: /data
{{- end }}
{{- if .Values.extraInitContainers }}
{{- toYaml .Values.extraInitContainers | nindent 6 }}
{{- end }}
containers:
- name: redis
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
{{- if .Values.redis.customCommand }}
{{ toYaml .Values.redis.customCommand | indent 10 }}
{{- else }}
- redis-server
{{- end }}
args:
{{- if .Values.redis.customArgs }}
{{ toYaml .Values.redis.customArgs | indent 10 }}
{{- else }}
- /data/conf/redis.conf
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
{{- if .Values.auth }}
env:
- name: AUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.existingSecret | default "" ) . }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.redis.envFrom }}
envFrom:
{{ toYaml .Values.redis.envFrom | indent 10 }}
{{- end }}
{{- if .Values.redis.livenessProbe.enabled }}
livenessProbe:
initialDelaySeconds: {{ .Values.redis.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.redis.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.redis.livenessProbe.timeoutSeconds }}
successThreshold: {{ .Values.redis.livenessProbe.successThreshold }}
failureThreshold: {{ .Values.redis.livenessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/redis_liveness.sh
{{- end }}
{{- if .Values.redis.readinessProbe.enabled }}
readinessProbe:
initialDelaySeconds: {{ .Values.redis.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.redis.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.redis.readinessProbe.timeoutSeconds }}
successThreshold: {{ .Values.redis.readinessProbe.successThreshold }}
failureThreshold: {{ .Values.redis.readinessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/redis_readiness.sh
{{- end }}
{{- if .Values.redis.startupProbe.enabled }}
startupProbe:
initialDelaySeconds: {{ .Values.redis.startupProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.redis.startupProbe.periodSeconds }}
timeoutSeconds: {{ .Values.redis.startupProbe.timeoutSeconds }}
successThreshold: {{ .Values.redis.startupProbe.successThreshold }}
failureThreshold: {{ .Values.redis.startupProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/redis_readiness.sh
{{- end }}
resources:
{{ toYaml .Values.redis.resources | indent 10 }}
ports:
{{- if ne (int .Values.redis.port) 0 }}
- name: redis
containerPort: {{ .Values.redis.port }}
{{- end }}
{{- if .Values.redis.tlsPort }}
- name: redis-tls
containerPort: {{ .Values.redis.tlsPort }}
{{- end }}
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
- mountPath: /data
name: data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end}}
- mountPath: /health
name: health
{{- if .Values.redis.extraVolumeMounts }}
{{- toYaml .Values.redis.extraVolumeMounts | nindent 8 }}
{{- end }}
lifecycle:
{{ toYaml .Values.redis.lifecycle | indent 10 }}
- name: sentinel
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
{{- if .Values.sentinel.customCommand }}
{{ toYaml .Values.sentinel.customCommand | indent 10 }}
{{- else }}
- redis-sentinel
{{- end }}
args:
{{- if .Values.sentinel.customArgs }}
{{ toYaml .Values.sentinel.customArgs | indent 10 }}
{{- else }}
- /data/conf/sentinel.conf
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
{{- if or .Values.auth .Values.sentinel.auth}}
env:
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.existingSecret | default "" ) . }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.sentinel.existingSecret | default "" ) . }}
name: {{ tpl (.Values.sentinel.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
{{- end }}
{{- if .Values.sentinel.livenessProbe.enabled }}
livenessProbe:
initialDelaySeconds: {{ .Values.sentinel.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.sentinel.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.sentinel.livenessProbe.timeoutSeconds }}
successThreshold: {{ .Values.sentinel.livenessProbe.successThreshold }}
failureThreshold: {{ .Values.sentinel.livenessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/sentinel_liveness.sh
{{- end }}
{{- if .Values.sentinel.readinessProbe.enabled }}
readinessProbe:
initialDelaySeconds: {{ .Values.sentinel.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.sentinel.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.sentinel.readinessProbe.timeoutSeconds }}
successThreshold: {{ .Values.sentinel.readinessProbe.successThreshold }}
failureThreshold: {{ .Values.sentinel.readinessProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/sentinel_liveness.sh
{{- end }}
{{- if .Values.sentinel.startupProbe.enabled }}
startupProbe:
initialDelaySeconds: {{ .Values.sentinel.startupProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.sentinel.startupProbe.periodSeconds }}
timeoutSeconds: {{ .Values.sentinel.startupProbe.timeoutSeconds }}
successThreshold: {{ .Values.sentinel.startupProbe.successThreshold }}
failureThreshold: {{ .Values.sentinel.startupProbe.failureThreshold }}
exec:
command:
- sh
- -c
- /health/sentinel_liveness.sh
{{- end }}
resources:
{{ toYaml .Values.sentinel.resources | indent 10 }}
ports:
{{- if ne (int .Values.sentinel.port) 0 }}
- name: sentinel
containerPort: {{ .Values.sentinel.port }}
{{- end }}
{{- if .Values.sentinel.tlsPort }}
- name: sentinel-tls
containerPort: {{ .Values.sentinel.tlsPort }}
{{- end }}
volumeMounts:
- mountPath: /data
name: data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end }}
- mountPath: /health
name: health
{{- if .Values.sentinel.extraVolumeMounts }}
{{- toYaml .Values.sentinel.extraVolumeMounts | nindent 8 }}
{{- end }}
lifecycle:
{{ toYaml .Values.sentinel.lifecycle | indent 10 }}
- name: split-brain-fix
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
livenessProbe: {{ .Values.splitBrainDetection.livenessProbe | toYaml | nindent 10 }}
readinessProbe: {{ .Values.splitBrainDetection.readinessProbe | toYaml | nindent 10 }}
resources: {{ toYaml .Values.splitBrainDetection.resources | nindent 10 }}
command:
- sh
args:
- /readonly-config/fix-split-brain.sh
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
env:
{{- $replicas := int (toString .Values.replicas) -}}
{{- range $i := until $replicas }}
- name: SENTINEL_ID_{{ $i }}
value: {{ printf "%s\n%s\nindex: %d" (include "redis-ha.name" $) ($.Release.Name) $i | sha256sum | trunc 40 }}
{{- end }}
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.existingSecret | default "" ) . }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.sentinel.existingSecret | default "" ) . }}
name: {{ tpl (.Values.sentinel.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
- mountPath: /data
name: data
{{- if .Values.redis.tlsPort }}
- mountPath: /tls-certs
name: tls-certs
{{- end }}
{{- if .Values.exporter.enabled }}
- name: redis-exporter
image: "{{ .Values.exporter.image }}:{{ .Values.exporter.tag }}"
imagePullPolicy: {{ .Values.exporter.pullPolicy }}
args:
{{- range $key, $value := .Values.exporter.extraArgs }}
- --{{ $key }}={{ $value }}
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 10 }}
env:
- name: REDIS_ADDR
{{- if .Values.exporter.sslEnabled }}
value: rediss://{{ default "localhost" .Values.exporter.address }}:{{ .Values.redis.tlsPort }}
{{- else }}
value: redis://{{ default "localhost" .Values.exporter.address }}:{{ .Values.redis.port }}
{{- end }}
{{- if .Values.auth }}
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
{{- if tpl (.Values.existingSecret | default "" ) . }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.exporter.script }}
- name: REDIS_EXPORTER_SCRIPT
value: /script/script.lua
{{- end }}
{{- if .Values.exporter.sslEnabled }}
- name: REDIS_EXPORTER_TLS_CLIENT_KEY_FILE
value: /tls-certs/{{ .Values.tls.keyFile }}
- name: REDIS_EXPORTER_TLS_CLIENT_CERT_FILE
value: /tls-certs/{{ .Values.tls.certFile }}
- name: REDIS_EXPORTER_TLS_CA_CERT_FILE
value: /tls-certs/{{ .Values.tls.caCertFile }}
{{- end }}
livenessProbe:
{{ toYaml .Values.exporter.livenessProbe | indent 10 }}
readinessProbe:
{{ toYaml .Values.exporter.readinessProbe | indent 10 }}
resources:
{{ toYaml .Values.exporter.resources | indent 10 }}
ports:
- name: {{ .Values.exporter.portName }}
containerPort: {{ .Values.exporter.port }}
volumeMounts:
{{- if .Values.exporter.script }}
- mountPath: /script
name: script-mount
{{- end }}
{{- if .Values.exporter.sslEnabled }}
- mountPath: /tls-certs
name: tls-certs
{{- end }}
{{- end }}
{{- if .Values.extraContainers }}
{{- toYaml .Values.extraContainers | nindent 6 }}
{{- end -}}
{{- with .Values.priorityClassName | default .Values.global.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ template "redis-ha.fullname" . }}-configmap
{{- if .Values.sysctlImage.mountHostSys }}
- name: host-sys
hostPath:
path: /sys
{{- end }}
{{- if .Values.exporter.script }}
- name: script-mount
configMap:
name: {{ template "redis-ha.fullname" . }}-exporter-script-configmap
items:
- key: script
path: script.lua
{{- end }}
{{- if .Values.redis.tlsPort }}
- name: tls-certs
secret:
{{- if tpl (.Values.tls.secretName | default "" ) . }}
secretName: {{ tpl (.Values.tls.secretName | default "" ) . }}
{{- else }}
secretName: {{ template "redis-ha.fullname" . }}-tls-secret
{{- end }}
{{- end }}
- name: health
configMap:
name: {{ template "redis-ha.fullname" . }}-health-configmap
defaultMode: 0755
{{- if .Values.extraVolumes }}
{{- toYaml .Values.extraVolumes | nindent 6 }}
{{- end -}}
{{- if .Values.persistentVolume.enabled }}
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
annotations:
{{- range $key, $value := .Values.persistentVolume.annotations }}
{{ $key }}: {{ $value }}
{{- end }}
labels: {{- toYaml .Values.persistentVolume.labels | nindent 8 }}
spec:
accessModes:
{{- range .Values.persistentVolume.accessModes }}
- {{ . | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistentVolume.size | quote }}
{{- if .Values.persistentVolume.storageClass }}
{{- if (eq "-" .Values.persistentVolume.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: "{{ .Values.persistentVolume.storageClass }}"
{{- end }}
{{- end }}
{{- else if .Values.hostPath.path }}
- name: data
hostPath:
path: {{ tpl .Values.hostPath.path .}}
{{- else }}
- name: data
emptyDir:
{{ toYaml .Values.emptyDir | indent 10 }}
{{- end }}
@@ -1,218 +0,0 @@
{{- if .Values.haproxy.enabled }}
kind: Deployment
apiVersion: apps/v1
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
component: haproxy
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- if .Values.haproxy.deploymentAnnotations }}
annotations:
{{- range $key, $value := .Values.haproxy.deploymentAnnotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
spec:
{{- with .Values.haproxy.deploymentStrategy }}
strategy:
{{- toYaml . | nindent 4 }}
{{- end }}
revisionHistoryLimit: 1
replicas: {{ .Values.haproxy.replicas }}
selector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
component: haproxy
template:
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
labels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
component: haproxy
{{- range $key, $value := .Values.haproxy.labels }}
{{ $key }}: {{ $value | toString }}
{{- end }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if and (.Values.haproxy.metrics.enabled) (not .Values.haproxy.metrics.serviceMonitor.enabled) }}
prometheus.io/port: "{{ .Values.haproxy.metrics.port }}"
prometheus.io/scrape: "true"
prometheus.io/path: "{{ .Values.haproxy.metrics.scrapePath }}"
{{- end }}
checksum/config: {{ print (include "config-haproxy.cfg" .) (include "config-haproxy_init.sh" .) | sha256sum }}
{{- if .Values.haproxy.podAnnotations }}
{{ toYaml .Values.haproxy.podAnnotations | indent 8 }}
{{- end }}
spec:
# Needed when using unmodified rbac-setup.yml
{{ if .Values.haproxy.serviceAccount.create }}
serviceAccountName: {{ template "redis-ha.serviceAccountName" . }}-haproxy
{{- else }}
serviceAccountName: {{ .Values.haproxy.serviceAccountName }}
{{- end }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.haproxy.securityContext "context" $) | nindent 8 }}
automountServiceAccountToken: {{ .Values.haproxy.serviceAccount.automountToken }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
tolerations:
{{ toYaml .Values.tolerations | indent 8 }}
affinity:
{{- if .Values.haproxy.affinity }}
{{- with .Values.haproxy.affinity }}
{{ tpl . $ | indent 8 }}
{{- end }}
{{- else }}
{{- if .Values.haproxy.additionalAffinities }}
{{ toYaml .Values.haproxy.additionalAffinities | indent 8 }}
{{- end }}
podAntiAffinity:
{{- if .Values.haproxy.hardAntiAffinity }}
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
component: haproxy
topologyKey: kubernetes.io/hostname
{{- else }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
component: haproxy
topologyKey: kubernetes.io/hostname
{{- end }}
{{- end }}
{{- if .Values.topologySpreadConstraints.enabled }}
topologySpreadConstraints:
- maxSkew: {{ .Values.topologySpreadConstraints.maxSkew | default 1 }}
topologyKey: {{ .Values.topologySpreadConstraints.topologyKey | default "topology.kubernetes.io/zone" }}
whenUnsatisfiable: {{ .Values.topologySpreadConstraints.whenUnsatisfiable | default "ScheduleAnyway" }}
labelSelector:
matchLabels:
app: {{ template "redis-ha.name" . }}-haproxy
release: {{ .Release.Name }}
component: haproxy
{{- end }}
initContainers:
- name: config-init
image: {{ .Values.haproxy.image.repository }}:{{ .Values.haproxy.image.tag }}
imagePullPolicy: {{ .Values.haproxy.image.pullPolicy }}
resources:
{{ toYaml .Values.haproxy.init.resources | indent 10 }}
command:
- sh
args:
- /readonly/haproxy_init.sh
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.haproxy.containerSecurityContext "context" $) | nindent 10 }}
volumeMounts:
- name: config-volume
mountPath: /readonly
readOnly: true
- name: data
mountPath: /data
{{- if .Values.haproxy.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.haproxy.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: haproxy
image: {{ .Values.haproxy.image.repository }}:{{ .Values.haproxy.image.tag }}
imagePullPolicy: {{ .Values.haproxy.image.pullPolicy }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.haproxy.containerSecurityContext "context" $) | nindent 10 }}
{{- if or .Values.auth .Values.sentinel.auth}}
env:
{{- if .Values.auth }}
- name: AUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.existingSecret | default "" ) . }}
name: {{ tpl (.Values.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}
{{- end }}
key: {{ .Values.authKey }}
{{- end }}
{{- if .Values.sentinel.auth }}
- name: SENTINELAUTH
valueFrom:
secretKeyRef:
{{- if tpl (.Values.sentinel.existingSecret | default "" ) . }}
name: {{ tpl (.Values.sentinel.existingSecret | default "" ) . }}
{{- else }}
name: {{ template "redis-ha.fullname" . }}-sentinel
{{- end }}
key: {{ .Values.sentinel.authKey }}
{{- end }}
{{- end }}
livenessProbe:
httpGet:
path: /healthz
port: probe
initialDelaySeconds: 5
periodSeconds: 3
readinessProbe:
httpGet:
path: /healthz
port: probe
initialDelaySeconds: 5
periodSeconds: 3
ports:
- name: probe
containerPort: 8888
- name: redis
containerPort: {{ default "6379" .Values.haproxy.containerPort }}
{{- if .Values.haproxy.readOnly.enabled }}
- name: readonlyport
containerPort: {{ default "6380" .Values.haproxy.readOnly.port }}
{{- end }}
{{- if .Values.haproxy.metrics.enabled }}
- name: metrics-port
containerPort: {{ default "9101" .Values.haproxy.metrics.port }}
{{- end }}
{{- range .Values.haproxy.additionalPorts }}
- name: {{ .name }}
containerPort: {{ .containerPort }}
{{- end }}
resources:
{{ toYaml .Values.haproxy.resources | indent 10 }}
volumeMounts:
- name: data
mountPath: /usr/local/etc/haproxy
- name: shared-socket
mountPath: /run/haproxy
{{- if .Values.haproxy.tls.enabled }}
- name: pemfile
mountPath: {{ .Values.haproxy.tls.certMountPath }}
{{- end }}
lifecycle:
{{ toYaml .Values.haproxy.lifecycle | indent 10 }}
{{- with .Values.haproxy.priorityClassName | default .Values.global.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
volumes:
{{- if .Values.haproxy.tls.enabled }}
- name: pemfile
secret:
secretName: {{ tpl .Values.haproxy.tls.secretName . }}
{{- end }}
- name: config-volume
configMap:
name: {{ template "redis-ha.fullname" . }}-configmap
- name: shared-socket
emptyDir:
{{ toYaml .Values.haproxy.emptyDir | indent 10 }}
- name: data
emptyDir:
{{ toYaml .Values.haproxy.emptyDir | indent 10 }}
{{- end }}
@@ -1,66 +0,0 @@
{{- if and .Values.haproxy.enabled .Values.haproxy.networkPolicy.enabled }}
{{- $root := . }}
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy-network-policy
namespace: {{ .Release.Namespace | quote }}
{{- if .Values.haproxy.networkPolicy.annotations }}
annotations:
{{- range $key, $value := .Values.haproxy.networkPolicy.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
labels:
{{- include "labels.standard" . | nindent 4 }}
{{- range $key, $value := .Values.haproxy.networkPolicy.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
component: haproxy
policyTypes:
- Ingress
- Egress
egress:
- to:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
{{- include "redis-ports" . | nindent 6 }}
- to:
- namespaceSelector: {}
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
{{- range $rule := .Values.haproxy.networkPolicy.egressRules }}
- to:
{{- (tpl (toYaml $rule.selectors) $) | nindent 8 }}
ports:
{{- toYaml $rule.ports | nindent 8 }}
{{- end }}
ingress:
- from:
- podSelector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}
ports:
{{- include "redis-ports" . | nindent 8 }}
{{- range $rule := .Values.haproxy.networkPolicy.ingressRules }}
- from:
{{- (tpl (toYaml $rule.selectors) $) | nindent 8 }}
ports:
{{- if $rule.ports }}
{{- toYaml $rule.ports | nindent 8 }}
{{- end }}
{{- include "redis-ports" $root | nindent 8 }}
{{- end }}
{{- end }}
@@ -1,19 +0,0 @@
{{- if .Values.haproxy.podDisruptionBudget -}}
apiVersion: {{ template "redis-ha.podDisruptionBudget.apiVersion" . }}
kind: PodDisruptionBudget
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy-pdb
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
selector:
matchLabels:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
component: haproxy
{{ toYaml .Values.haproxy.podDisruptionBudget | indent 2 }}
{{- end -}}
@@ -1,22 +0,0 @@
{{- if .Values.haproxy.enabled }}
{{- if and .Values.haproxy.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
component: haproxy
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
rules:
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
{{- end }}
{{- end }}
@@ -1,22 +0,0 @@
{{- if .Values.haproxy.enabled }}
{{- if and .Values.haproxy.serviceAccount.create .Values.rbac.create }}
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
component: haproxy
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
subjects:
- kind: ServiceAccount
name: {{ template "redis-ha.serviceAccountName" . }}-haproxy
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ template "redis-ha.fullname" . }}-haproxy
{{- end }}
{{- end }}
@@ -1,75 +0,0 @@
{{- if .Values.haproxy.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
component: haproxy
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.haproxy.service.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
annotations:
{{- if .Values.haproxy.service.annotations }}
{{ toYaml .Values.haproxy.service.annotations | indent 4 }}
{{- end }}
spec:
type: {{ default "ClusterIP" .Values.haproxy.service.type }}
{{- if .Values.haproxy.service.clusterIP }}
clusterIP: {{ .Values.haproxy.service.clusterIP }}
{{- end }}
{{- if .Values.haproxy.service.ipFamilyPolicy }}
ipFamilyPolicy: {{ .Values.haproxy.service.ipFamilyPolicy }}
{{- end }}
{{- if .Values.haproxy.service.ipFamilies }}
ipFamilies: {{ toYaml .Values.haproxy.service.ipFamilies | nindent 2 }}
{{- end }}
{{- if and (eq .Values.haproxy.service.type "LoadBalancer") .Values.haproxy.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.haproxy.service.loadBalancerIP }}
{{- end }}
{{- if and (eq .Values.haproxy.service.type "LoadBalancer") .Values.haproxy.service.externalTrafficPolicy }}
externalTrafficPolicy: {{ .Values.haproxy.service.externalTrafficPolicy }}
{{- end }}
{{- if and (eq .Values.haproxy.service.type "LoadBalancer") .Values.haproxy.service.loadBalancerSourceRanges }}
loadBalancerSourceRanges: {{ toYaml .Values.haproxy.service.loadBalancerSourceRanges | nindent 2 }}
{{- end }}
{{- if .Values.haproxy.service.externalIPs }}
externalIPs:
{{- range $key, $value := .Values.haproxy.service.externalIPs }}
- {{ $value }}
{{- end }}
{{- end }}
ports:
- name: tcp-haproxy
port: {{ .Values.haproxy.servicePort }}
protocol: TCP
targetPort: redis
{{- if and (eq .Values.haproxy.service.type "NodePort") .Values.haproxy.service.nodePort }}
nodePort: {{ .Values.haproxy.service.nodePort }}
{{- end }}
{{- if .Values.haproxy.readOnly.enabled }}
- name: tcp-haproxyreadonly
port: {{ .Values.haproxy.readOnly.port }}
protocol: TCP
targetPort: {{ .Values.haproxy.readOnly.port }}
{{- end }}
{{- if .Values.haproxy.metrics.enabled }}
- name: {{ .Values.haproxy.metrics.portName }}
port: {{ .Values.haproxy.metrics.port }}
protocol: TCP
targetPort: metrics-port
{{- end }}
{{- range .Values.haproxy.additionalPorts }}
- name: {{ .name }}
port: {{ .servicePort | default .containerPort }}
protocol: TCP
targetPort: {{ .name }}
{{- end }}
selector:
release: {{ .Release.Name }}
app: {{ template "redis-ha.name" . }}-haproxy
{{- end }}
@@ -1,15 +0,0 @@
{{- if and .Values.haproxy.serviceAccount.create .Values.haproxy.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ template "redis-ha.serviceAccountName" . }}-haproxy
namespace: {{ .Release.Namespace | quote }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "redis-ha.fullname" . }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
@@ -1,39 +0,0 @@
{{- if and ( or .Values.haproxy.metrics.serviceMonitor.disableAPICheck ( .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" ) ) ( .Values.haproxy.metrics.serviceMonitor.enabled ) ( .Values.haproxy.metrics.enabled ) }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ template "redis-ha.fullname" . }}-haproxy
namespace: {{ .Values.haproxy.metrics.serviceMonitor.namespace | default .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.haproxy.metrics.serviceMonitor.labels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
spec:
endpoints:
- targetPort: {{ .Values.haproxy.metrics.port }}
{{- if .Values.haproxy.metrics.serviceMonitor.interval }}
interval: {{ .Values.haproxy.metrics.serviceMonitor.interval }}
{{- end }}
{{- if .Values.haproxy.metrics.serviceMonitor.telemetryPath }}
path: {{ .Values.haproxy.metrics.serviceMonitor.telemetryPath }}
{{- end }}
{{- if .Values.haproxy.metrics.serviceMonitor.timeout }}
scrapeTimeout: {{ .Values.haproxy.metrics.serviceMonitor.timeout }}
{{- end }}
{{- with .Values.haproxy.metrics.serviceMonitor.endpointAdditionalProperties }}
{{- toYaml . | nindent 4 }}
{{- end }}
jobLabel: {{ template "redis-ha.fullname" . }}-haproxy
namespaceSelector:
matchNames:
- {{ .Release.Namespace | quote }}
selector:
matchLabels:
app: {{ template "redis-ha.name" . }}
release: {{ .Release.Name }}
component: haproxy
{{- end }}
@@ -1,27 +0,0 @@
{{- if and .Values.redis.tlsPort (not (tpl (.Values.tls.secretName | default "" ) . )) -}}
apiVersion: v1
kind: Secret
metadata:
metadata:
name: {{ template "redis-ha.fullname" . }}-tls-secret
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{- if .Values.tls.caCertFile }}
{{ .Values.tls.caCertFile }}: {{ .Files.Get "certs/ca.crt" | b64enc }}
{{- end }}
{{- if .Values.tls.certFile }}
{{ .Values.tls.certFile }}: {{ .Files.Get "certs/redis.crt" | b64enc }}
{{- end }}
{{- if .Values.tls.keyFile }}
{{ .Values.tls.keyFile }}: {{ .Files.Get "certs/redis.key" | b64enc }}
{{- end }}
{{- if .Values.tls.dhParamsFile }}
{{ .Values.tls.dhParamsFile }}: {{ .Files.Get "certs/redis.dh" | b64enc }}
{{- end }}
{{- end }}
@@ -1,15 +0,0 @@
{{- if and .Values.sentinel.auth (not (tpl ( .Values.sentinel.existingSecret | default "" ) . )) -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "redis-ha.fullname" . }}-sentinel
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
{{- range $key, $value := .Values.extraLabels }}
{{ $key }}: {{ $value | quote }}
{{- end }}
type: Opaque
data:
{{ .Values.sentinel.authKey }}: {{ .Values.sentinel.password | b64enc | quote }}
{{- end -}}
@@ -1,32 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: {{ template "redis-ha.fullname" . }}-configmap-test
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
annotations:
"helm.sh/hook": test-success
spec:
nodeSelector: {{ toYaml .Values.nodeSelector | nindent 4 }}
tolerations: {{ toYaml .Values.tolerations | nindent 4 }}
containers:
- name: check-init
image: {{ .Values.configmapTest.image.repository }}:{{ .Values.configmapTest.image.tag }}
args:
- --shell=sh
- /readonly-config/init.sh
volumeMounts:
- name: config
mountPath: /readonly-config
readOnly: true
resources: {{ toYaml .Values.configmapTest.resources | nindent 6 }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 6 }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 4 }}
{{- end }}
restartPolicy: Never
volumes:
- name: config
configMap:
name: {{ template "redis-ha.fullname" . }}-configmap
@@ -1,32 +0,0 @@
{{- if .Values.haproxy.enabled }}
apiVersion: v1
kind: Pod
metadata:
name: {{ template "redis-ha.fullname" . }}-service-test
namespace: {{ .Release.Namespace | quote }}
labels:
{{ include "labels.standard" . | indent 4 }}
annotations:
{{- if .Values.podAnnotations }}
{{ toYaml .Values.podAnnotations | indent 4 }}
{{- end }}
"helm.sh/hook": test-success
spec:
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 4 }}
tolerations:
{{ toYaml .Values.tolerations | indent 4 }}
containers:
- name: "{{ .Release.Name }}-service-test"
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
command:
- sh
- -c
- redis-cli -h {{ template "redis-ha.fullname" . }}-haproxy -p {{ .Values.redis.port }} info server
resources: {{ toYaml .Values.haproxy.tests.resources | nindent 6 }}
securityContext: {{- include "compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) | nindent 6 }}
{{- if .Values.imagePullSecrets }}
imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 4 }}
{{- end }}
restartPolicy: Never
{{- end }}
File diff suppressed because it is too large. Load diff
@@ -1,37 +0,0 @@
{{- if .Values.notifications.logLevel }}
DEPRECATED option notifications.logLevel - Use `configs.params."notificationscontroller.log.level"`
{{- end }}
{{- if .Values.notifications.logFormat }}
DEPRECATED option notifications.logFormat - Use `configs.params."notificationscontroller.log.format"`
{{- end }}
{{- if .Values.dex.logLevel }}
DEPRECATED option dex.logLevel - Use `configs.params."dexserver.log.level"`
{{- end }}
{{- if .Values.dex.logFormat }}
DEPRECATED option dex.logFormat - Use `configs.params."dexserver.log.format"`
{{- end }}
In order to access the server UI you have the following options:
{{ $rootpath := default "" (index .Values "configs" "params" "server.rootpath") -}}
1. kubectl port-forward service/{{ include "argo-cd.fullname" . }}-server -n {{ include "argo-cd.namespace" . }} 8080:443
{{ if $rootpath }}
and then open the browser on http://localhost:8080/{{ $rootpath }} and accept the certificate
{{ else }}
and then open the browser on http://localhost:8080 and accept the certificate
{{ end }}
2. enable ingress in the values file `server.ingress.enabled` and either
- Add the annotation for ssl passthrough: https://argo-cd.readthedocs.io/en/stable/operator-manual/ingress/#option-1-ssl-passthrough
- Set the `configs.params."server.insecure"` in the values file and terminate SSL at your ingress: https://argo-cd.readthedocs.io/en/stable/operator-manual/ingress/#option-2-multiple-ingress-objects-and-hosts
{{ if eq (toString (index .Values.configs.cm "admin.enabled")) "true" -}}
After reaching the UI the first time you can login with username: admin and the random password generated during the installation. You can find the password by running:
kubectl -n {{ include "argo-cd.namespace" . }} get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
(You should delete the initial secret afterwards as suggested by the Getting Started Guide: https://argo-cd.readthedocs.io/en/stable/getting_started/#4-login-using-the-cli)
{{ else if or (index .Values.configs.cm "dex.config") (index .Values.configs.cm "oidc.config") -}}
After reaching the UI the first time you can login using Dex or OIDC.
{{ else -}}
After reaching the UI the first time you cannot login with username and password since you've disabled it. You should enable admin back or configure Dex via `configs.cm.dex.config` or OIDC via `configs.cm.oidc.config`.
{{ end -}}
@@ -1,147 +0,0 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "argo-cd.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "argo-cd.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "argo-cd.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create Argo CD app version
*/}}
{{- define "argo-cd.defaultTag" -}}
{{- default .Chart.AppVersion .Values.global.image.tag }}
{{- end -}}
{{/*
Return valid version label
*/}}
{{- define "argo-cd.versionLabelValue" -}}
{{ regexReplaceAll "[^-A-Za-z0-9_.]" (include "argo-cd.defaultTag" .) "-" | trunc 63 | trimAll "-" | trimAll "_" | trimAll "." | quote }}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "argo-cd.labels" -}}
helm.sh/chart: {{ include "argo-cd.chart" .context }}
{{ include "argo-cd.selectorLabels" (dict "context" .context "component" .component "name" .name) }}
app.kubernetes.io/managed-by: {{ .context.Release.Service }}
app.kubernetes.io/part-of: argocd
app.kubernetes.io/version: {{ include "argo-cd.versionLabelValue" .context }}
{{- with .context.Values.global.additionalLabels }}
{{ toYaml . }}
{{- end }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "argo-cd.selectorLabels" -}}
{{- if .name -}}
app.kubernetes.io/name: {{ include "argo-cd.name" .context }}-{{ .name }}
{{ end -}}
app.kubernetes.io/instance: {{ .context.Release.Name }}
{{- if .component }}
app.kubernetes.io/component: {{ .component }}
{{- end }}
{{- end }}
{{/*
Common affinity definition
Pod affinity
- Soft prefers different nodes
- Hard requires different nodes and prefers different availibility zones
Node affinity
- Soft prefers given user expressions
- Hard requires given user expressions
*/}}
{{- define "argo-cd.affinity" -}}
{{- with .component.affinity -}}
{{- toYaml . -}}
{{- else -}}
{{- $preset := .context.Values.global.affinity -}}
{{- if (eq $preset.podAntiAffinity "soft") }}
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: {{ include "argo-cd.name" .context }}-{{ .component.name }}
topologyKey: kubernetes.io/hostname
{{- else if (eq $preset.podAntiAffinity "hard") }}
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/name: {{ include "argo-cd.name" .context }}-{{ .component.name }}
topologyKey: topology.kubernetes.io/zone
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: {{ include "argo-cd.name" .context }}-{{ .component.name }}
topologyKey: kubernetes.io/hostname
{{- end }}
{{- with $preset.nodeAffinity.matchExpressions }}
{{- if (eq $preset.nodeAffinity.type "soft") }}
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
{{- toYaml . | nindent 6 }}
{{- else if (eq $preset.nodeAffinity.type "hard") }}
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Common deployment strategy definition
- Recreate don't have additional fields, we need to remove them if added by the mergeOverwrite
*/}}
{{- define "argo-cd.strategy" -}}
{{- $preset := . -}}
{{- if (eq (toString $preset.type) "Recreate") }}
type: Recreate
{{- else if (eq (toString $preset.type) "RollingUpdate") }}
type: RollingUpdate
{{- with $preset.rollingUpdate }}
rollingUpdate:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end }}
{{- end -}}
@@ -1,347 +0,0 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Create controller name and version as used by the chart label.
Truncated at 52 chars because StatefulSet label 'controller-revision-hash' is limited
to 63 chars and it includes 10 chars of hash and a separating '-'.
*/}}
{{- define "argo-cd.controller.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.controller.name | trunc 52 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the controller service account to use
*/}}
{{- define "argo-cd.controller.serviceAccountName" -}}
{{- if .Values.controller.serviceAccount.create -}}
{{ default (include "argo-cd.controller.fullname" .) .Values.controller.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.controller.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create dex name and version as used by the chart label.
*/}}
{{- define "argo-cd.dex.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.dex.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create Dex server endpoint
*/}}
{{- define "argo-cd.dex.server" -}}
{{- $insecure := index .Values.configs.params "dexserver.disable.tls" | toString -}}
{{- $scheme := (eq $insecure "true") | ternary "http" "https" -}}
{{- $host := include "argo-cd.dex.fullname" . -}}
{{- $port := int .Values.dex.servicePortHttp -}}
{{- printf "%s://%s:%d" $scheme $host $port }}
{{- end }}
{{/*
Create the name of the dex service account to use
*/}}
{{- define "argo-cd.dex.serviceAccountName" -}}
{{- if .Values.dex.serviceAccount.create -}}
{{ default (include "argo-cd.dex.fullname" .) .Values.dex.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.dex.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create redis name and version as used by the chart label.
*/}}
{{- define "argo-cd.redis.fullname" -}}
{{- $redisHa := (index .Values "redis-ha") -}}
{{- $redisHaContext := dict "Chart" (dict "Name" "redis-ha") "Release" .Release "Values" $redisHa -}}
{{- if $redisHa.enabled -}}
{{- if $redisHa.haproxy.enabled -}}
{{- printf "%s-haproxy" (include "redis-ha.fullname" $redisHaContext) | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- include "redis-ha.fullname" $redisHaContext | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- else -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.redis.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{/*
Return Redis server endpoint
*/}}
{{- define "argo-cd.redis.server" -}}
{{- $redisHa := (index .Values "redis-ha") -}}
{{- if or (and .Values.redis.enabled (not $redisHa.enabled)) (and $redisHa.enabled $redisHa.haproxy.enabled) }}
{{- printf "%s:%s" (include "argo-cd.redis.fullname" .) (toString .Values.redis.servicePort) }}
{{- else if and .Values.externalRedis.host .Values.externalRedis.port }}
{{- printf "%s:%s" .Values.externalRedis.host (toString .Values.externalRedis.port) }}
{{- end }}
{{- end -}}
{{/*
Create the name of the redis service account to use
*/}}
{{- define "argo-cd.redis.serviceAccountName" -}}
{{- if .Values.redis.serviceAccount.create -}}
{{ default (include "argo-cd.redis.fullname" .) .Values.redis.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.redis.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create Redis secret-init name
*/}}
{{- define "argo-cd.redisSecretInit.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.redisSecretInit.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the Redis secret-init service account to use
*/}}
{{- define "argo-cd.redisSecretInit.serviceAccountName" -}}
{{- if .Values.redisSecretInit.serviceAccount.create -}}
{{ default (include "argo-cd.redisSecretInit.fullname" .) .Values.redisSecretInit.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.redisSecretInit.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create argocd server name and version as used by the chart label.
*/}}
{{- define "argo-cd.server.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.server.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the Argo CD server service account to use
*/}}
{{- define "argo-cd.server.serviceAccountName" -}}
{{- if .Values.server.serviceAccount.create -}}
{{ default (include "argo-cd.server.fullname" .) .Values.server.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.server.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create argocd repo-server name and version as used by the chart label.
*/}}
{{- define "argo-cd.repoServer.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.repoServer.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the repo-server service account to use
*/}}
{{- define "argo-cd.repoServer.serviceAccountName" -}}
{{- if .Values.repoServer.serviceAccount.create -}}
{{ default (include "argo-cd.repoServer.fullname" .) .Values.repoServer.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.repoServer.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create argocd application set name and version as used by the chart label.
*/}}
{{- define "argo-cd.applicationSet.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.applicationSet.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the application set service account to use
*/}}
{{- define "argo-cd.applicationSet.serviceAccountName" -}}
{{- if .Values.applicationSet.serviceAccount.create -}}
{{ default (include "argo-cd.applicationSet.fullname" .) .Values.applicationSet.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.applicationSet.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create argocd notifications name and version as used by the chart label.
*/}}
{{- define "argo-cd.notifications.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.notifications.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the notifications service account to use
*/}}
{{- define "argo-cd.notifications.serviceAccountName" -}}
{{- if .Values.notifications.serviceAccount.create -}}
{{ default (include "argo-cd.notifications.fullname" .) .Values.notifications.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.notifications.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Create argocd commit-server name and version as used by the chart label.
*/}}
{{- define "argo-cd.commitServer.fullname" -}}
{{- printf "%s-%s" (include "argo-cd.fullname" .) .Values.commitServer.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create the name of the commit-server service account to use
*/}}
{{- define "argo-cd.commitServer.serviceAccountName" -}}
{{- if .Values.commitServer.serviceAccount.create -}}
{{ default (include "argo-cd.commitServer.fullname" .) .Values.commitServer.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.commitServer.serviceAccount.name }}
{{- end -}}
{{- end -}}
{{/*
Argo Configuration Preset Values (Influenced by Values configuration)
*/}}
{{- define "argo-cd.config.cm.presets" -}}
{{- $presets := dict -}}
{{- $_ := set $presets "url" (printf "https://%s" .Values.global.domain) -}}
{{- if eq (toString (index .Values.configs.cm "statusbadge.enabled")) "true" -}}
{{- $_ := set $presets "statusbadge.url" (printf "https://%s/" .Values.global.domain) -}}
{{- end -}}
{{- if .Values.configs.styles -}}
{{- $_ := set $presets "ui.cssurl" "./custom/custom.styles.css" -}}
{{- end -}}
{{- toYaml $presets }}
{{- end -}}
{{/*
Merge Argo Configuration with Preset Configuration
*/}}
{{- define "argo-cd.config.cm" -}}
{{- $config := omit .Values.configs.cm "create" "annotations" "resourceExclusionsAdditional" -}}
{{- $preset := include "argo-cd.config.cm.presets" . | fromYaml | default dict -}}
{{- $merged := mergeOverwrite $preset $config -}}
{{- if .Values.configs.cm.resourceExclusionsAdditional }}
{{- $existing := get $merged "resource.exclusions" | default "" | trimSuffix "\n" }}
{{- $additional := .Values.configs.cm.resourceExclusionsAdditional | toYaml }}
{{- $_ := set $merged "resource.exclusions" (list $existing $additional | compact | join "\n") -}}
{{- end }}
{{- range $key, $value := $merged }}
{{- $fmted := $value | toString }}
{{- if not (eq $fmted "") }}
{{ $key }}: {{ $fmted | toYaml }}
{{- end }}
{{- end }}
{{- end -}}
{{/*
Argo Params Default Configuration Presets
NOTE: Configuration keys must be stored as dict because YAML treats dot as separator
*/}}
{{- define "argo-cd.config.params.presets" -}}
{{- $presets := dict -}}
{{- $_ := set $presets "repo.server" (printf "%s:%s" (include "argo-cd.repoServer.fullname" .) (.Values.repoServer.service.port | toString)) -}}
{{- $_ := set $presets "server.repo.server.strict.tls" (.Values.repoServer.certificateSecret.enabled | toString ) -}}
{{- $_ := set $presets "redis.server" (include "argo-cd.redis.server" .) -}}
{{- $_ := set $presets "applicationsetcontroller.enable.leader.election" (gt ((.Values.applicationSet.replicas | default .Values.applicationSet.replicaCount) | int64) 1) -}}
{{- if .Values.dex.enabled -}}
{{- $_ := set $presets "server.dex.server" (include "argo-cd.dex.server" .) -}}
{{- $_ := set $presets "server.dex.server.strict.tls" .Values.dex.certificateSecret.enabled -}}
{{- end -}}
{{- if .Values.commitServer.enabled -}}
{{- $_ := set $presets "commit.server" (printf "%s:%s" (include "argo-cd.commitServer.fullname" .) (.Values.commitServer.service.port | toString)) -}}
{{- end -}}
{{- range $component := tuple "applicationsetcontroller" "controller" "server" "reposerver" "notificationscontroller" "dexserver" "commitserver" -}}
{{- $_ := set $presets (printf "%s.log.format" $component) $.Values.global.logging.format -}}
{{- $_ := set $presets (printf "%s.log.level" $component) $.Values.global.logging.level -}}
{{- end -}}
{{- toYaml $presets }}
{{- end -}}
{{/*
Merge Argo Params Configuration with Preset Configuration
*/}}
{{- define "argo-cd.config.params" -}}
{{- $config := omit .Values.configs.params "create" "annotations" }}
{{- $preset := include "argo-cd.config.params.presets" . | fromYaml | default dict -}}
{{- range $key, $value := mergeOverwrite $preset $config }}
{{ $key }}: {{ toString $value | toYaml }}
{{- end }}
{{- end -}}
{{/*
Expand the namespace of the release.
Allows overriding it for multi-namespace deployments in combined charts.
*/}}
{{- define "argo-cd.namespace" -}}
{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}}
{{- end }}
{{/*
Dual stack definition
*/}}
{{- define "argo-cd.dualStack" -}}
{{- with .Values.global.dualStack.ipFamilyPolicy }}
ipFamilyPolicy: {{ . }}
{{- end }}
{{- with .Values.global.dualStack.ipFamilies }}
ipFamilies: {{ toYaml . | nindent 4 }}
{{- end }}
{{- end }}
{{/*
secretKeyRef of env variable REDIS_USERNAME
*/}}
{{- define "argo-cd.redisUsernameSecretRef" -}}
{{- if .Values.externalRedis.host -}}
name: {{ default "argocd-redis" .Values.externalRedis.existingSecret }}
key: redis-username
optional: {{ if .Values.externalRedis.username }}false{{ else }}true{{ end }}
{{- else -}}
name: "argocd-redis"
key: redis-username
optional: true
{{- end -}}
{{- end -}}
{{/*
secretKeyRef of env variable REDIS_PASSWORD
*/}}
{{- define "argo-cd.redisPasswordSecretRef" -}}
{{- if .Values.externalRedis.host -}}
{{- /* External Redis use case */ -}}
{{- /* Secret is required when specifying existingSecret or a password, otherwise it is optional */ -}}
name: {{ default "argocd-redis" .Values.externalRedis.existingSecret }}
key: redis-password
optional: {{ if or .Values.externalRedis.existingSecret .Values.externalRedis.password }}false{{ else }}true{{ end }}
{{- else if and .Values.redisSecretInit.enabled -}}
{{- /* Default case where Secret is generated by the Job with Helm pre-install hooks */ -}}
name: "argocd-redis" # hard-coded in Job command and embedded Redis deployments (standalone and redis-ha)
key: auth
optional: false # Secret is not optional in this case !
{{- else -}}
{{- /* All other use cases (e.g. disabled pre-install Job) */ -}}
name: "argocd-redis"
key: auth
optional: true
{{- end -}}
{{- end -}}
{{/*
Return the target Kubernetes version
*/}}
{{- define "argo-cd.kubeVersion" -}}
{{- default .Capabilities.KubeVersion.Version .Values.kubeVersionOverride }}
{{- end -}}
{{/*
Return the appropriate apiVersion for monitoring CRDs
*/}}
{{- define "argo-cd.apiVersions.monitoring" -}}
{{- if .Values.apiVersionOverrides.monitoring -}}
{{- print .Values.apiVersionOverrides.monitoring -}}
{{- else -}}
{{- print "monitoring.coreos.com/v1" -}}
{{- end -}}
{{- end -}}
@@ -1,7 +0,0 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Return the target Kubernetes version
*/}}
{{- define "argo-cd.kubeVersion" -}}
{{- default .Capabilities.KubeVersion.Version .Values.kubeVersionOverride }}
{{- end }}
@@ -1,70 +0,0 @@
{{- if .Values.createAggregateRoles }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "argo-cd.fullname" . }}-aggregate-to-view
labels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
{{- include "argo-cd.labels" (dict "context" .) | nindent 4 }}
rules:
- apiGroups:
- argoproj.io
resources:
- applications
- applicationsets
- appprojects
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "argo-cd.fullname" . }}-aggregate-to-edit
labels:
rbac.authorization.k8s.io/aggregate-to-edit: "true"
{{- include "argo-cd.labels" (dict "context" .) | nindent 4 }}
rules:
- apiGroups:
- argoproj.io
resources:
- applications
- applicationsets
- appprojects
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "argo-cd.fullname" . }}-aggregate-to-admin
labels:
rbac.authorization.k8s.io/aggregate-to-admin: "true"
{{- include "argo-cd.labels" (dict "context" .) | nindent 4 }}
rules:
- apiGroups:
- argoproj.io
resources:
- applications
- applicationsets
- appprojects
verbs:
- create
- delete
- deletecollection
- get
- list
- patch
- update
- watch
{{- end }}
@@ -1,23 +0,0 @@
{{- if .Values.createClusterRoles }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "argo-cd.controller.fullname" . }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
rules:
{{- if .Values.controller.clusterRoleRules.enabled }}
{{- toYaml .Values.controller.clusterRoleRules.rules | nindent 2 }}
{{- else }}
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
{{- end }}
{{- end }}
@@ -1,16 +0,0 @@
{{- if .Values.createClusterRoles }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "argo-cd.controller.fullname" . }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "argo-cd.controller.fullname" . }}
subjects:
- kind: ServiceAccount
name: {{ include "argo-cd.controller.serviceAccountName" . }}
namespace: {{ include "argo-cd.namespace" . }}
{{- end }}
@@ -1,459 +0,0 @@
{{- if .Values.controller.dynamicClusterDistribution }}
apiVersion: apps/v1
kind: Deployment
metadata:
{{- with (mergeOverwrite (deepCopy .Values.global.deploymentAnnotations) .Values.controller.deploymentAnnotations) }}
annotations:
{{- range $key, $value := . }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
name: {{ template "argo-cd.controller.fullname" . }}
namespace: {{ include "argo-cd.namespace" . }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
{{- with (mergeOverwrite (deepCopy .Values.global.deploymentLabels) .Values.controller.deploymentLabels) }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: {{ .Values.controller.replicas }}
revisionHistoryLimit: {{ .Values.controller.revisionHistoryLimit | default .Values.global.revisionHistoryLimit }}
selector:
matchLabels:
{{- include "argo-cd.selectorLabels" (dict "context" . "name" .Values.controller.name) | nindent 6 }}
template:
metadata:
annotations:
checksum/cmd-params: {{ include (print $.Template.BasePath "/argocd-configs/argocd-cmd-params-cm.yaml") . | sha256sum }}
{{- if .Values.configs.cm.create }}
checksum/cm: {{ include (print $.Template.BasePath "/argocd-configs/argocd-cm.yaml") . | sha256sum }}
{{- end }}
{{- with (mergeOverwrite (deepCopy .Values.global.podAnnotations) .Values.controller.podAnnotations) }}
{{- range $key, $value := . }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 8 }}
{{- with (mergeOverwrite (deepCopy .Values.global.podLabels) .Values.controller.podLabels) }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.controller.runtimeClassName | default .Values.global.runtimeClassName }}
runtimeClassName: {{ . }}
{{- end }}
{{- with .Values.controller.imagePullSecrets | default .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.global.hostAliases }}
hostAliases:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.global.securityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.controller.priorityClassName | default .Values.global.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
{{- if .Values.controller.terminationGracePeriodSeconds }}
terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }}
{{- end }}
serviceAccountName: {{ include "argo-cd.controller.serviceAccountName" . }}
automountServiceAccountToken: {{ .Values.controller.automountServiceAccountToken }}
containers:
- args:
- /usr/local/bin/argocd-application-controller
- --metrics-port={{ .Values.controller.containerPorts.metrics }}
{{- if .Values.controller.metrics.applicationLabels.enabled }}
{{- range .Values.controller.metrics.applicationLabels.labels }}
- --metrics-application-labels
- {{ . }}
{{- end }}
{{- end }}
{{- with .Values.controller.extraArgs }}
{{- toYaml . | nindent 8 }}
{{- end }}
image: {{ default .Values.global.image.repository .Values.controller.image.repository }}:{{ default (include "argo-cd.defaultTag" .) .Values.controller.image.tag }}
imagePullPolicy: {{ default .Values.global.image.imagePullPolicy .Values.controller.image.imagePullPolicy }}
name: {{ .Values.controller.name }}
env:
{{- with (concat .Values.global.env .Values.controller.env) }}
{{- tpl (toYaml .) $ | nindent 10 }}
{{- end }}
- name: ARGOCD_ENABLE_DYNAMIC_CLUSTER_DISTRIBUTION
value: "true"
- name: ARGOCD_CONTROLLER_HEARTBEAT_TIME
value: {{ .Values.controller.heartbeatTime | quote }}
- name: ARGOCD_APPLICATION_CONTROLLER_NAME
value: {{ template "argo-cd.controller.fullname" . }}
- name: ARGOCD_RECONCILIATION_TIMEOUT
valueFrom:
configMapKeyRef:
name: argocd-cm
key: timeout.reconciliation
optional: true
- name: ARGOCD_HARD_RECONCILIATION_TIMEOUT
valueFrom:
configMapKeyRef:
name: argocd-cm
key: timeout.hard.reconciliation
optional: true
- name: ARGOCD_RECONCILIATION_JITTER
valueFrom:
configMapKeyRef:
key: timeout.reconciliation.jitter
name: argocd-cm
optional: true
- name: ARGOCD_REPO_ERROR_GRACE_PERIOD_SECONDS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.repo.error.grace.period.seconds
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: repo.server
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.repo.server.timeout.seconds
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.status.processors
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.operation.processors
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_LOGFORMAT
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.log.format
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_LOGLEVEL
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.log.level
optional: true
- name: ARGOCD_LOG_FORMAT_TIMESTAMP
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: log.format.timestamp
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.metrics.cache.expiration
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.self.heal.timeout.seconds
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_TIMEOUT_SECONDS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.self.heal.backoff.timeout.seconds
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_FACTOR
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.self.heal.backoff.factor
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_CAP_SECONDS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.self.heal.backoff.cap.seconds
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_BACKOFF_COOLDOWN_SECONDS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.self.heal.backoff.cooldown.seconds
optional: true
- name: ARGOCD_SYNC_WAVE_DELAY
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.sync.wave.delay.seconds
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_SYNC_TIMEOUT
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.sync.timeout.seconds
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.repo.server.plaintext
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.repo.server.strict.tls
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_PERSIST_RESOURCE_HEALTH
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.resource.health.persist
optional: true
- name: ARGOCD_APP_STATE_CACHE_EXPIRATION
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.app.state.cache.expiration
optional: true
- name: REDIS_SERVER
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: redis.server
optional: true
- name: REDIS_COMPRESSION
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: redis.compression
optional: true
- name: REDISDB
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: redis.db
optional: true
- name: REDIS_USERNAME
valueFrom:
secretKeyRef:
{{- include "argo-cd.redisUsernameSecretRef" . | nindent 16 }}
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
{{- include "argo-cd.redisPasswordSecretRef" . | nindent 16 }}
- name: REDIS_SENTINEL_USERNAME
valueFrom:
secretKeyRef:
name: {{ default (include "argo-cd.redis.fullname" .) .Values.externalRedis.existingSecret }}
key: redis-sentinel-username
optional: true
- name: REDIS_SENTINEL_PASSWORD
valueFrom:
secretKeyRef:
name: {{ default (include "argo-cd.redis.fullname" .) .Values.externalRedis.existingSecret }}
key: redis-sentinel-password
optional: true
- name: ARGOCD_DEFAULT_CACHE_EXPIRATION
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.default.cache.expiration
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_ADDRESS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: otlp.address
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_INSECURE
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: otlp.insecure
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_OTLP_HEADERS
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: otlp.headers
optional: true
- name: ARGOCD_APPLICATION_NAMESPACES
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: application.namespaces
optional: true
- name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.sharding.algorithm
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_KUBECTL_PARALLELISM_LIMIT
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.kubectl.parallelism.limit
optional: true
- name: ARGOCD_K8SCLIENT_RETRY_MAX
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.k8sclient.retry.max
optional: true
- name: ARGOCD_K8SCLIENT_RETRY_BASE_BACKOFF
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.k8sclient.retry.base.backoff
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.diff.server.side
optional: true
- name: ARGOCD_IGNORE_NORMALIZER_JQ_TIMEOUT
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.ignore.normalizer.jq.timeout
optional: true
- name: ARGOCD_HYDRATOR_ENABLED
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: hydrator.enabled
optional: true
- name: ARGOCD_CLUSTER_CACHE_BATCH_EVENTS_PROCESSING
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.cluster.cache.batch.events.processing
optional: true
- name: ARGOCD_CLUSTER_CACHE_EVENTS_PROCESSING_INTERVAL
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: controller.cluster.cache.events.processing.interval
optional: true
- name: ARGOCD_APPLICATION_CONTROLLER_COMMIT_SERVER
valueFrom:
configMapKeyRef:
name: argocd-cmd-params-cm
key: commit.server
optional: true
{{- with .Values.controller.envFrom }}
envFrom:
{{- toYaml . | nindent 10 }}
{{- end }}
ports:
- name: metrics
containerPort: {{ .Values.controller.containerPorts.metrics }}
protocol: TCP
readinessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: {{ .Values.controller.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.controller.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.controller.readinessProbe.timeoutSeconds }}
successThreshold: {{ .Values.controller.readinessProbe.successThreshold }}
failureThreshold: {{ .Values.controller.readinessProbe.failureThreshold }}
resources:
{{- toYaml .Values.controller.resources | nindent 10 }}
{{- with .Values.controller.containerSecurityContext }}
securityContext:
{{- toYaml . | nindent 10 }}
{{- end }}
workingDir: /home/argocd
volumeMounts:
{{- with (concat .Values.global.extraVolumeMounts .Values.controller.volumeMounts) }}
{{- toYaml . | nindent 8 }}
{{- end }}
- mountPath: /app/config/controller/tls
name: argocd-repo-server-tls
- mountPath: /home/argocd
name: argocd-home
- name: argocd-cmd-params-cm
mountPath: /home/argocd/params
{{- with .Values.controller.extraContainers }}
{{- tpl (toYaml .) $ | nindent 6 }}
{{- end }}
{{- with .Values.controller.initContainers }}
initContainers:
{{- tpl (toYaml .) $ | nindent 6 }}
{{- end }}
{{- with include "argo-cd.affinity" (dict "context" . "component" .Values.controller) }}
affinity:
{{- trim . | nindent 8 }}
{{- end }}
{{- with .Values.controller.nodeSelector | default .Values.global.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.controller.tolerations | default .Values.global.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.controller.topologySpreadConstraints | default .Values.global.topologySpreadConstraints }}
topologySpreadConstraints:
{{- range $constraint := . }}
- {{ toYaml $constraint | nindent 8 | trim }}
{{- if not $constraint.labelSelector }}
labelSelector:
matchLabels:
{{- include "argo-cd.selectorLabels" (dict "context" $ "name" $.Values.controller.name) | nindent 12 }}
{{- end }}
{{- end }}
{{- end }}
volumes:
{{- with (concat .Values.global.extraVolumes .Values.controller.volumes) }}
{{- toYaml . | nindent 6 }}
{{- end }}
- name: argocd-home
{{- if .Values.controller.emptyDir.sizeLimit }}
emptyDir:
sizeLimit: {{ .Values.controller.emptyDir.sizeLimit }}
{{- else }}
emptyDir: {}
{{- end }}
- name: argocd-repo-server-tls
secret:
secretName: argocd-repo-server-tls
optional: true
items:
- key: tls.crt
path: tls.crt
- key: tls.key
path: tls.key
- key: ca.crt
path: ca.crt
- name: argocd-cmd-params-cm
configMap:
optional: true
name: argocd-cmd-params-cm
items:
- key: controller.profile.enabled
path: profiler.enabled
{{- if .Values.controller.hostNetwork }}
hostNetwork: {{ .Values.controller.hostNetwork }}
{{- end }}
{{- with .Values.controller.dnsConfig }}
dnsConfig:
{{- toYaml . | nindent 8 }}
{{- end }}
dnsPolicy: {{ .Values.controller.dnsPolicy }}
{{- end }}
@@ -1,35 +0,0 @@
{{- if .Values.controller.metrics.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "argo-cd.controller.fullname" . }}-metrics
namespace: {{ include "argo-cd.namespace" . }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" "metrics") | nindent 4 }}
{{- with .Values.controller.metrics.service.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if or .Values.controller.metrics.service.annotations .Values.global.addPrometheusAnnotations }}
annotations:
{{- if .Values.global.addPrometheusAnnotations }}
prometheus.io/port: {{ .Values.controller.metrics.service.servicePort | quote }}
prometheus.io/scrape: "true"
{{- end }}
{{- range $key, $value := .Values.controller.metrics.service.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
spec:
type: {{ .Values.controller.metrics.service.type }}
{{- if and .Values.controller.metrics.service.clusterIP (eq .Values.controller.metrics.service.type "ClusterIP") }}
clusterIP: {{ .Values.controller.metrics.service.clusterIP }}
{{- end }}
{{- include "argo-cd.dualStack" . | indent 2 }}
ports:
- name: {{ .Values.controller.metrics.service.portName }}
protocol: TCP
port: {{ .Values.controller.metrics.service.servicePort }}
targetPort: metrics
selector:
{{- include "argo-cd.selectorLabels" (dict "context" . "name" .Values.controller.name) | nindent 4 }}
{{- end }}
@@ -1,20 +0,0 @@
{{- if or .Values.controller.networkPolicy.create .Values.global.networkPolicy.create }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
name: {{ template "argo-cd.controller.fullname" . }}
namespace: {{ include "argo-cd.namespace" . }}
spec:
ingress:
- from:
- namespaceSelector: {}
ports:
- port: metrics
podSelector:
matchLabels:
{{- include "argo-cd.selectorLabels" (dict "context" . "name" .Values.controller.name) | nindent 6 }}
policyTypes:
- Ingress
{{- end }}
@@ -1,27 +0,0 @@
{{- if .Values.controller.pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "argo-cd.controller.fullname" . }}
namespace: {{ include "argo-cd.namespace" . }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
{{- with .Values.controller.pdb.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.controller.pdb.annotations }}
annotations:
{{- range $key, $value := . }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
spec:
{{- with .Values.controller.pdb.maxUnavailable }}
maxUnavailable: {{ . }}
{{- else }}
minAvailable: {{ .Values.controller.pdb.minAvailable | default 0 }}
{{- end }}
selector:
matchLabels:
{{- include "argo-cd.selectorLabels" (dict "context" . "name" .Values.controller.name) | nindent 6 }}
{{- end }}
@@ -1,25 +0,0 @@
{{- $apiVersion := include "argo-cd.apiVersions.monitoring" . }}
{{- if and (.Capabilities.APIVersions.Has "monitoring.coreos.com/v1") .Values.controller.metrics.enabled .Values.controller.metrics.rules.enabled }}
apiVersion: {{ $apiVersion }}
kind: PrometheusRule
metadata:
name: {{ template "argo-cd.controller.fullname" . }}
namespace: {{ default (include "argo-cd.namespace" .) .Values.controller.metrics.rules.namespace | quote }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
{{- if .Values.controller.metrics.rules.selector }}
{{- toYaml .Values.controller.metrics.rules.selector | nindent 4 }}
{{- end }}
{{- if .Values.controller.metrics.rules.additionalLabels }}
{{- toYaml .Values.controller.metrics.rules.additionalLabels | nindent 4 }}
{{- end }}
{{- with .Values.controller.metrics.rules.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
groups:
- name: argocd
rules:
{{- toYaml .Values.controller.metrics.rules.spec | nindent 4 }}
{{- end }}
@@ -1,64 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ template "argo-cd.controller.fullname" . }}
namespace: {{ include "argo-cd.namespace" . }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
rules:
{{- with .Values.controller.roleRules }}
{{- toYaml . | nindent 2 }}
{{- else }}
- apiGroups:
- ""
resources:
- secrets
- configmaps
verbs:
- get
- list
- watch
- apiGroups:
- argoproj.io
resources:
- applications
- applicationsets
- appprojects
verbs:
- create
- get
- list
- watch
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- list
- apiGroups:
- apps
resources:
- deployments
verbs:
- get
- list
- watch
{{- if and (not .Values.createClusterRoles) .Values.controller.dynamicClusterDistribution }}
- apiGroups:
- ""
resources:
- configmaps
resourceNames:
- argocd-app-controller-shard-cm
verbs:
- get
- list
- watch
- create
- update
{{- end }}
{{- end }}
@@ -1,15 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "argo-cd.controller.fullname" . }}
namespace: {{ include "argo-cd.namespace" . }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "argo-cd.controller.fullname" . }}
subjects:
- kind: ServiceAccount
name: {{ include "argo-cd.controller.serviceAccountName" . }}
namespace: {{ include "argo-cd.namespace" . }}
@@ -1,19 +0,0 @@
{{- if .Values.controller.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
automountServiceAccountToken: {{ .Values.controller.serviceAccount.automountServiceAccountToken }}
metadata:
name: {{ include "argo-cd.controller.serviceAccountName" . }}
namespace: {{ include "argo-cd.namespace" . }}
{{- with .Values.controller.serviceAccount.annotations }}
annotations:
{{- range $key, $value := . }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
{{- with .Values.controller.serviceAccount.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
@@ -1,52 +0,0 @@
{{- $apiVersion := include "argo-cd.apiVersions.monitoring" . }}
{{- if and (.Capabilities.APIVersions.Has $apiVersion) .Values.controller.metrics.enabled .Values.controller.metrics.serviceMonitor.enabled }}
apiVersion: {{ $apiVersion }}
kind: ServiceMonitor
metadata:
name: {{ template "argo-cd.controller.fullname" . }}
namespace: {{ default (include "argo-cd.namespace" .) .Values.controller.metrics.serviceMonitor.namespace | quote }}
labels:
{{- include "argo-cd.labels" (dict "context" . "component" .Values.controller.name "name" .Values.controller.name) | nindent 4 }}
{{- with .Values.controller.metrics.serviceMonitor.selector }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.controller.metrics.serviceMonitor.additionalLabels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.controller.metrics.serviceMonitor.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
endpoints:
- port: {{ .Values.controller.metrics.service.portName }}
{{- with .Values.controller.metrics.serviceMonitor.interval }}
interval: {{ . }}
{{- end }}
{{- with .Values.controller.metrics.serviceMonitor.scrapeTimeout }}
scrapeTimeout: {{ . }}
{{- end }}
path: /metrics
{{- with .Values.controller.metrics.serviceMonitor.relabelings }}
relabelings:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.controller.metrics.serviceMonitor.metricRelabelings }}
metricRelabelings:
{{- toYaml . | nindent 8 }}
{{- end }}
honorLabels: {{ .Values.controller.metrics.serviceMonitor.honorLabels }}
{{- with .Values.controller.metrics.serviceMonitor.scheme }}
scheme: {{ . }}
{{- end }}
{{- with .Values.controller.metrics.serviceMonitor.tlsConfig }}
tlsConfig:
{{- toYaml . | nindent 8 }}
{{- end }}
namespaceSelector:
matchNames:
- {{ include "argo-cd.namespace" . }}
selector:
matchLabels:
{{- include "argo-cd.selectorLabels" (dict "context" . "component" .Values.controller.name "name" "metrics") | nindent 6 }}
{{- end }}
Loaded 100 of 358 files, more files were not shown because too many files have changed in this diff. Show more