@@ -140,23 +140,121 @@ volsync-backup/
140140│ └── snapshots/ # Snapshot metadata
141141```
142142
143+ ## Critical Implementation Details
144+
145+ ### Kyverno Policy: Operations Filter (Race Condition Fix)
146+
147+ **Problem:** Without an operations filter, Kyverno intercepts ALL PVC operations including DELETE, preventing PVC deletion.
148+
149+ **Solution:** All rules must include `operations: [CREATE]`:
150+
151+ ```yaml
152+ match:
153+ any:
154+ - resources:
155+ kinds:
156+ - PersistentVolumeClaim
157+ operations:
158+ - CREATE # CRITICAL: Only trigger on create, not delete
159+ selector:
160+ matchExpressions:
161+ - key: backup
162+ operator: In
163+ values: ["hourly", "daily"]
164+ ```
165+
166+ ### External Secrets: mergePolicy (Template + Data Merge)
167+
168+ ** Problem:** Kyverno uses ` {{ }} ` syntax, External Secrets uses ` {{ }} ` syntax. They conflict.
169+
170+ ** Solution:** Use ` mergePolicy: Merge ` to combine:
171+ - ` template.data ` - RESTIC_REPOSITORY (Kyverno substitutes namespace/pvc-name)
172+ - ` data ` section - Credentials fetched from 1Password
173+
174+ ``` yaml
175+ target :
176+ name : " volsync-{{request.object.metadata.name}}"
177+ creationPolicy : Owner
178+ template :
179+ engineVersion : v2
180+ mergePolicy : Merge # CRITICAL: Merge template with fetched data
181+ data :
182+ # Kyverno substitutes these variables at generate time
183+ RESTIC_REPOSITORY : " s3:http://192.168.10.133:30292/volsync-backup/{{request.object.metadata.namespace}}/{{request.object.metadata.name}}"
184+ data :
185+ # External Secrets fetches these from 1Password and merges them
186+ - secretKey : AWS_ACCESS_KEY_ID
187+ remoteRef :
188+ key : rustfs
189+ property : k8s-admin-access-key
190+ - secretKey : AWS_SECRET_ACCESS_KEY
191+ remoteRef :
192+ key : rustfs
193+ property : k8s-admin-secret-key
194+ - secretKey : RESTIC_PASSWORD
195+ remoteRef :
196+ key : rustfs
197+ property : restic_password
198+ ` ` `
199+
200+ **Result:** Secret contains all 4 required fields:
201+ - ` RESTIC_REPOSITORY` - from template (Kyverno-substituted)
202+ - ` AWS_ACCESS_KEY_ID` - from 1Password
203+ - ` AWS_SECRET_ACCESS_KEY` - from 1Password
204+ - ` RESTIC_PASSWORD` - from 1Password
205+
206+ # ## Kyverno RBAC Requirements
207+
208+ Kyverno needs permissions to generate ExternalSecrets and VolSync resources :
209+
210+ ` ` ` yaml
211+ # infrastructure/controllers/kyverno/rbac-patch.yaml
212+ rules:
213+ - apiGroups: [""]
214+ resources: ["secrets"]
215+ verbs: ["get", "list", "watch", "create", "update", "delete"]
216+ - apiGroups: ["external-secrets.io"]
217+ resources: ["externalsecrets"]
218+ verbs: ["get", "list", "watch", "create", "update", "delete"]
219+ - apiGroups: ["volsync.backube"]
220+ resources: ["replicationsources", "replicationdestinations"]
221+ verbs: ["get", "list", "watch", "create", "update", "delete"]
222+ ` ` `
223+
143224# # Troubleshooting
144225
145226# ## PVC Stuck in Pending
1462271. Check if ReplicationDestination exists : ` kubectl get replicationdestination -n <namespace>`
1472282. Check pvc-plumber logs : ` kubectl logs -n volsync-system -l app.kubernetes.io/name=pvc-plumber`
1482293. Check VolSync mover pod : ` kubectl get pods -n <namespace> | grep volsync`
149230
231+ # ## PVC Stuck in Terminating (Race Condition)
232+ **Symptom:** PVCs won't delete, Kyverno keeps intercepting patches.
233+
234+ **Cause:** Missing `operations: [CREATE]` filter in Kyverno policy.
235+
236+ **Fix:** Ensure all rules have `operations: [CREATE]` in match clause.
237+
238+ # ## Secret Missing Credentials
239+ **Symptom:** VolSync fails with "access_key: placeholder" or missing credentials.
240+
241+ **Cause:** `mergePolicy: Replace` (default) overwrites fetched data with template.
242+
243+ **Fix:** Add `mergePolicy: Merge` to ExternalSecret template.
244+
245+ **Verify:** `kubectl get secret volsync-<pvc-name> -n <namespace> -o json | jq '.data | keys'`
246+ Should show : ` ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "RESTIC_PASSWORD", "RESTIC_REPOSITORY"]`
247+
150248# ## Backup Not Running
1512491. Check ReplicationSource : ` kubectl get replicationsource -n <namespace>`
1522502. Check secret exists : ` kubectl get secret -n <namespace> | grep volsync`
1532513. Check ExternalSecret status : ` kubectl get externalsecret -n <namespace>`
154252
155253# ## Test pvc-plumber
156254` ` ` bash
157- kubectl port-forward -n volsync-system svc/pvc-plumber 8080:80
158- curl http://localhost:8080 /exists/karakeep/data-pvc
159- # Expected: {"exists":true} or {"exists":false}
255+ kubectl run -n volsync-system test --rm -it --image=curlimages/curl --restart=Never -- \
256+ curl -s http://pvc-plumber.volsync-system.svc.cluster.local /exists/karakeep/data-pvc
257+ # Expected: {"exists":true,"keyCount":1 } or {"exists":false}
160258` ` `
161259
162260# # Excluded Namespaces
0 commit comments