mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-31 17:17:26 +09:00
fix(mariadb): re-guard replication so single-replica passes the operator webhook
PR #2279 made `replication.enabled: true` unconditional so the operator would always create -primary/-secondary services. But the mariadb-operator validating webhook (vmariadb-v1alpha1.kb.io) rejects replication when spec.replicas == 1, so single-replica MariaDB could no longer be created — the HelmRelease never became Ready. Re-guard replication on replicas>1 and teach the service-name consumers about the bare <release> service the operator provisions in both topologies: - mariadb.yaml: gate `replication` on replicas>1; restore single-replica external access via `service.type: LoadBalancer` and keep `primaryService` LoadBalancer for replicas>1 only. - backup-cronjob.yaml: route single-replica dumps to the bare service (no -secondary exists without replication). - dashboard-resourcemap.yaml: always grant RBAC on the bare service, plus -primary/-secondary when replicas>1, matching the ApplicationDefinition. - mariadb-rd: list the bare service in services.include so single-replica installs are visible in the dashboard. Adds a single-replica e2e test that reproduces the webhook rejection and guards against regression. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Алексей Артамонов <alexeyartamonov1987@gmail.com>
This commit is contained in:
1 parent
fdbe6b299c
commit
c7e8f4d975
5 files changed
+75
-2
No files matched your search
@@ -49,3 +49,62 @@ EOF
|
||||
kubectl -n tenant-test wait deployment.apps/mariadb-$name-metrics --timeout=90s --for=jsonpath='{.status.replicas}'=1
|
||||
kubectl -n tenant-test delete mariadbs.apps.cozystack.io $name
|
||||
}
|
||||
|
||||
@test "Create single-replica MariaDB" {
|
||||
name='single'
|
||||
kubectl -n tenant-test delete mariadbs.apps.cozystack.io $name --ignore-not-found --timeout=2m
|
||||
kubectl apply -f- <<EOF
|
||||
apiVersion: apps.cozystack.io/v1alpha1
|
||||
kind: MariaDB
|
||||
metadata:
|
||||
name: $name
|
||||
namespace: tenant-test
|
||||
spec:
|
||||
external: false
|
||||
size: 10Gi
|
||||
replicas: 1
|
||||
storageClass: ""
|
||||
users:
|
||||
testuser:
|
||||
maxUserConnections: 1000
|
||||
password: xai7Wepo
|
||||
databases:
|
||||
testdb:
|
||||
roles:
|
||||
admin:
|
||||
- testuser
|
||||
backup:
|
||||
enabled: false
|
||||
s3Region: us-east-1
|
||||
s3Bucket: s3.example.org/mariadb-backups
|
||||
schedule: "0 2 * * *"
|
||||
cleanupStrategy: "--keep-last=3 --keep-daily=3 --keep-within-weekly=1m"
|
||||
s3AccessKey: oobaiRus9pah8PhohL1ThaeTa4UVa7gu
|
||||
s3SecretKey: ju3eum4dekeich9ahM1te8waeGai0oog
|
||||
resticPassword: ChaXoveekoh6eigh4siesheeda2quai0
|
||||
resources: {}
|
||||
resourcesPreset: "nano"
|
||||
EOF
|
||||
# Wait for the operator to materialise the HelmRelease before kubectl wait
|
||||
# kicks in (kubectl wait errors immediately if the object does not exist yet).
|
||||
timeout 60 sh -ec "until kubectl -n tenant-test get hr mariadb-$name >/dev/null 2>&1; do sleep 2; done"
|
||||
# A single-replica MariaDB must be accepted by the operator's validating
|
||||
# webhook: replication is guarded on replicas>1, so replicas=1 renders a CR
|
||||
# the webhook admits. Dump the HR + rendered CR on failure so a webhook
|
||||
# rejection (the regression this guards against) is legible in the log.
|
||||
kubectl -n tenant-test wait hr mariadb-$name --timeout=5m --for=condition=ready \
|
||||
|| { echo "HR mariadb-$name not ready — dumping state:"; \
|
||||
kubectl -n tenant-test describe hr mariadb-$name; \
|
||||
kubectl -n tenant-test get mariadbs.k8s.mariadb.com $name -o yaml; false; }
|
||||
# With replicas=1 the operator provisions the bare <name> service (no
|
||||
# -primary/-secondary): assert it exists and has an endpoint.
|
||||
timeout 80 sh -ec "until kubectl -n tenant-test get svc mariadb-$name -o jsonpath='{.spec.ports[0].port}' | grep -q '3306'; do sleep 10; done"
|
||||
timeout 80 sh -ec "until kubectl -n tenant-test get endpoints mariadb-$name -o jsonpath='{.subsets[*].addresses[*].ip}' | grep -q '[0-9]'; do sleep 10; done"
|
||||
timeout 60 sh -ec "until kubectl -n tenant-test get statefulset.apps/mariadb-$name >/dev/null 2>&1; do sleep 2; done"
|
||||
kubectl -n tenant-test wait statefulset.apps/mariadb-$name --timeout=110s --for=jsonpath='{.status.replicas}'=1
|
||||
timeout 80 sh -ec "until kubectl -n tenant-test get svc mariadb-$name-metrics -o jsonpath='{.spec.ports[0].port}' | grep -q '9104'; do sleep 10; done"
|
||||
timeout 40 sh -ec "until kubectl -n tenant-test get endpoints mariadb-$name-metrics -o jsonpath='{.subsets[*].addresses[*].ip}' | grep -q '[0-9]'; do sleep 10; done"
|
||||
timeout 60 sh -ec "until kubectl -n tenant-test get deployment.apps/mariadb-$name-metrics >/dev/null 2>&1; do sleep 2; done"
|
||||
kubectl -n tenant-test wait deployment.apps/mariadb-$name-metrics --timeout=90s --for=jsonpath='{.status.replicas}'=1
|
||||
kubectl -n tenant-test delete mariadbs.apps.cozystack.io $name --ignore-not-found
|
||||
}
|
||||
@@ -41,7 +41,9 @@ spec:
|
||||
name: {{ .Release.Name }}
|
||||
key: root-password
|
||||
- name: MYSQL_HOST
|
||||
value: "{{ .Release.Name }}-{{ if eq (int .Values.replicas) 1 }}primary{{ else }}secondary{{ end }}"
|
||||
# replicas>1 enables replication (offload dumps to a secondary);
|
||||
# replicas=1 has no replication and only the bare service exists.
|
||||
value: "{{ .Release.Name }}{{- if gt (int .Values.replicas) 1 }}-secondary{{- end }}"
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
|
||||
@@ -8,8 +8,14 @@ rules:
|
||||
resources:
|
||||
- services
|
||||
resourceNames:
|
||||
# The bare <name> service exists in both topologies; -primary/-secondary
|
||||
# only when replication is on (replicas>1). Grant exactly what exists so the
|
||||
# RBAC matches the services the ApplicationDefinition (mariadb-rd) lists.
|
||||
- {{ .Release.Name }}
|
||||
{{- if gt (int .Values.replicas) 1 }}
|
||||
- {{ .Release.Name }}-primary
|
||||
- {{ .Release.Name }}-secondary
|
||||
{{- end }}
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- ""
|
||||
|
||||
@@ -29,11 +29,13 @@ spec:
|
||||
- {{ .Release.Name }}
|
||||
topologyKey: "kubernetes.io/hostname"
|
||||
|
||||
{{- if gt (int .Values.replicas) 1 }}
|
||||
replication:
|
||||
enabled: true
|
||||
#primary:
|
||||
# podIndex: 0
|
||||
# automaticFailover: true
|
||||
{{- end }}
|
||||
|
||||
podMetadata:
|
||||
labels:
|
||||
@@ -71,6 +73,9 @@ spec:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
{{- if and .Values.external (eq (int .Values.replicas) 1) }}
|
||||
type: LoadBalancer
|
||||
{{- end }}
|
||||
storage:
|
||||
size: {{ .Values.size }}
|
||||
resizeInUseVolumes: true
|
||||
@@ -79,7 +84,7 @@ spec:
|
||||
storageClassName: {{ . }}
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.external }}
|
||||
{{- if and .Values.external (gt (int .Values.replicas) 1) }}
|
||||
primaryService:
|
||||
type: LoadBalancer
|
||||
{{- end }}
|
||||
|
||||
@@ -35,5 +35,6 @@ spec:
|
||||
exclude: []
|
||||
include:
|
||||
- resourceNames:
|
||||
- mariadb-{{ .name }}
|
||||
- mariadb-{{ .name }}-primary
|
||||
- mariadb-{{ .name }}-secondary
|
||||
Reference in new issue
Block a user