Commit 6ff08cb
authored
Release 11 juni: API-key 401 fix, taaklog-fixes, orphan/backup-sweeps, SOPS skip, domeinvalidatie (#125)
* feat(reconciliation): rapport-eerst service-orphan sweep met purge-veiligheidslagen
Implementeert features/service-orphan-reconciliation.md en repareert de
fundamenten van de reconciliation die het nooit voorbij dry-run schopte:
1. _build_expected_resources leest nu alle schema-generaties: catalog-
componenten/helm-charts/helmfiles (zelfde resolutie als de delete-flow)
PLUS legacy deployment-level blokken, met echte servicenamen
(postgresql-database, minio-storage) en clone-generaties (_vN op de
database, nooit op de user). Voorheen was de verwachte set leeg voor
vrijwel elk project, waardoor de restored->unmark beveiliging dood was
(waggl-9et near-miss: live prod-DB 30 dagen gemarkeerd).
2. Purge-veiligheidslagen, gecontroleerd op purge-moment:
- mark waarvan de resource in de verwachte set zit -> unmark, nooit purge
(geldt nu ook voor project-scoped cleanup/trigger, dat blind purgde)
- database met actieve connecties -> geweigerd + gerapporteerd; alleen
expliciete projectverwijdering mag connecties termineren
3. Read-only sweep (GET /api/v2/admin/orphans/report): inventariseert
databases, Keycloak realms/clients en MinIO-buckets, classificeert
expected/system/orphan_candidate/in_use_anomaly/unknown. Gevalideerd
tegen de echte productie-inventaris (55 DB's, 374 clients): waggl en
regel_k4c_pr748_v1 correct expected, 54 regel-k4c + 20 wies dode
PR-clients als kandidaat, custom clients veilig unknown.
4. Bevestiging (POST /api/v2/admin/orphans/confirm): expliciete lijst,
server-side hervalidatie (alleen actueel orphan_candidate), daarna
normale grace-periode. Nieuw resource-type keycloak_client met eigen
purge-handler (realm in mark-metadata).
Automatisch markeren vanuit een scan blijft bewust verboden.
* docs(orphan-sweep): documenteer unknown-classificatie van verwijderde projecten (bouwm) + geparkeerd git-history-idee
* Weekly updated
* fix(tasks): voorkom valse 'stuck RUNNING' meldingen en maak change-logs leesbaar
Vier observability-fixes naar aanleiding van de 530-delete nacht van 11 juni:
1. Succesvolle async tasks markeerden de legacy in-memory _projects entry
nooit COMPLETED (alleen fail_project en de backup-handler deden dat),
waardoor elke geslaagde task 2 uur later als 'stuck RUNNING' werd
gerapporteerd. De worker synct nu elk terminaal pad via
mark_legacy_completed/mark_legacy_failed; de sweep-warning noemt
voortaan projectnaam en betekenis.
2. Change-analysis logt deployment-namen in plaats van kale DeepDiff-paden
('deployments.7' wordt 'deployments.7 (pr-372)') en de samenvatting
meldt expliciet wanneer een schema auto-migratie de eenmalige
alle-deployments diff veroorzaakte.
3. Commit-messages van image-bumps vergeleken de nieuwe image met
project_data dat al gemuteerd was, waardoor elke bump het generieke
': configuration' kreeg. De delen worden nu tijdens de update-loop
verzameld, voor de overschrijving.
4. extract_deployment_components logde een identieke DEBUG-regel per
aanroep (14x per deployment per run); nu een keer per deployment per
handler-instantie.
* fix(auth): decrypt api-key bij herregistratie via load_project_from_data
Portal-saves (modal wizard, wizard, subdomain admin, backup tasks)
herregistreren een project via load_project_from_data, dat config.api-key
rauw uit de YAML registreerde - de AGE-ciphertext. Elke API-key-vergelijking
verwacht plaintext, dus een portal-save vergiftigde de in-memory key en
401'de alle API-calls van het project (~1 minuut, tot de save-getriggerde
process-run de gedecrypte key herregistreerde). Zo verloor de wies pr-394
CI-deploy van 11 juni 11:30 de race met een gelijktijdige portal-edit:
upsert geslaagd, maar de eerstvolgende task-poll kreeg 401 met een
misleidende 'verify your ZAD_API_KEY' melding.
load_project_from_data decrypt de api-key nu voor registratie (zelfde
keten als ProjectManager.get_api_key: globale key -> project-key ->
api-key). Bij decrypt-falen blijft de eerder geregistreerde plaintext
staan zodat een mislukte save authenticatie niet kan breken; plaintext
waarden (legacy/test) passeren ongewijzigd.
* feat(backup): daily retention sweep for orphaned snapshots
Kopia retention only runs at the end of a backup, scoped to that run's
source identity, so snapshots of deployments that no longer get backed up
(deleted deployments, removed schedules, broken legacy uid@podname
identities) never expire. Add a daily sweep that classifies every source
and deletes orphaned non-manual snapshots past a grace period.
- BACKUP_SWEEP_ENABLED / BACKUP_SWEEP_DRY_RUN / BACKUP_ORPHAN_RETENTION_DAYS
- dry-run by default: logs a manifest, deletes nothing until reviewed
- manual backups and actively-scheduled sources are never touched
- thread kopia source identity (user@host) through to SnapshotInfo
- bulk delete_snapshots over a single repo connection
* fix(backup): don't clone backup schedule into new deployments
upsert_deployment with cloneFrom deep-copies the source deployment's
config. backup was missing from the exclude list, so every PR preview
cloned from production inherited the nightly schedule and accumulated
database snapshots that nothing ever pruned. Add backup to
clone_exclude_keys; the data clone (clone-from) is unaffected.
* feat(sops): skip re-encryption when secret plaintext is unchanged
SOPS encryption is non-deterministic (fresh nonces/MAC/lastmodified per
run), so re-encrypting unchanged secrets rewrote every *.sops.yaml on
every deployment and churned the GitOps repos. encrypt_to_sops_files now
takes an optional private_key: it decrypts the existing ciphertext,
compares parsed YAML against the freshly generated plaintext, and keeps
the existing file verbatim when unchanged (dropping only the plaintext
source). Fails safe by re-encrypting on any doubt: no existing file,
decrypt failure, unparseable YAML, or no key.
Threaded through encrypt_to_sops_files_or_fail and all managers: project
secrets/helm/helmfile/infra use the project key via _sops_private_key_for
(None for legacy projects), ArgoCD repo secrets use SOPS_AGE_PRIVATE_KEY.
Adds real sops/age round-trip tests and a feature doc.
* fix(domains): cluster-default resolutie + domeinvalidatie in de API-upsert
Twee samenhangende domein-fixes uit de wizard/editables-hoek:
1. ClusterDefaultDomain resolvede naar het eerste nice-URL domein in
plaats van het ingress-default domein van het cluster. Gevolg: de
request-checkbox verscheen onterecht voor de clusterstandaard en de
PRE_SAVE hook materialiseerde een verkeerd base-domain (plus phantom
domain request) in het projectbestand. De resolver geeft nu het
ingress-postfix domein terug en faalt hard op een onbekende
CLUSTER_MANAGER; de hook schrijft de clusterstandaard nooit meer uit
(afwezigheid van base-domain betekent 'gebruik de default').
2. De API-upsert draait nu dezelfde DomainConfigEnforcer als de wizard,
zodat een dot-formaat URL niet gecombineerd kan worden met een
dash-only domein (onbereikbare multi-label hostname). Alleen actief
wanneer de call domeinvelden wijzigt, zodat image-only updates nooit
stuklopen op bestaande configuratie.
* docs(openproject): EE-token is hostnamegebonden — sandbox en productie elk een eigen token
* docs(weekly): redactieslag 11 juni
* docs(weekly): tweede release van 11 juni toegevoegd
* test(wizard): cluster-default base-domain creates no phantom domain request
The pre-existing test_flow_with_none_base_domain_resolves_default asserted
the OLD buggy behaviour: leaving the base-domain select on 'cluster default'
(empty) materialised the last-selected nice-URL domain plus a phantom domain
request, deploying to a domain the user never chose. The committed wizard fix
(ClusterDefaultDomain resolves the ingress default; _resolve_missing_base_domains
does not materialise it) correctly stops that. is_deployment_domain_approved
returns True for an empty base-domain, so the cluster default needs no request.
Rewritten as a regression guard: cluster default stays None and creates no
domains entry.
* test(orphans): make confirm-endpoint test robust to settings-reference pollution
test_only_current_orphan_candidates_accepted passed in isolation but failed in
the full suite: admin_router and endpoint_util bind 'settings' by reference at
import, and in the full suite they were first-imported while another test had
opi.core.config.settings replaced (mock_settings fixture) or reloaded
(test_secret_key_failclosed). That left their module-level reference pointing at
a stale mock, surfacing as a 501 (ADMIN_API_KEY unset) and then a MagicMock in
the JSON response (DELETION_GRACE_PERIOD_DAYS). Re-point both module references
to the live settings before patching the attributes the endpoint reads.
* fix(reconciliation+domains): sluit twee review-gaten van de 11-juni release
Twee correctheids-/veiligheidsgaten gevonden bij de review van deze
release-bundel, plus de bijbehorende nice-to-haves.
1. _build_expected_resources resolvede de database-generatie altijd onder
POSTGRESQL_DATABASE, terwijl een namespace-postgres deployment zijn
generatie onder NAMESPACE_POSTGRESQL_DATABASE opslaat. Voor een
gekloonde/herstelde namespace-postgres DB viel daardoor de _vN-naam uit
de verwachte set en kon de purge-tijd unmark-beveiliging de live
database niet meer beschermen (waggl-9et-klasse). Nu wordt tegen beide
DB-servicetypes geresolved.
2. De DomainConfigEnforcer draaide de domein-goedkeuringscheck alleen voor
het wizard-sentinel "__custom__". Een directe API-upsert met een
letterlijk, niet-ondersteund base-domain omzeilde de check volledig en
kon zo een domein buiten het cluster (of van een andere tenant) in het
projectbestand zetten. De check geldt nu voor elk niet-platform domein,
ongeacht herkomst; API-callers kunnen geen aanvraag-vinkje zetten dus
een onbevestigd domein wordt hard geweigerd (conform de docstring).
Nice-to-haves uit dezelfde review:
- keycloak_client expliciet gedocumenteerd als bewust afwezig in de
verwachte set (leunt op de confirm-hervalidatie, niet op unmark).
- backup-retention-sweep doc: waarschuwing dat backup.enabled: false alle
geplande snapshots na de grace-periode laat verwijderen.
- sops.py: function-local "import yaml" naar de top verplaatst.
Regressietests toegevoegd voor beide gaten (falen op de oude code,
slagen nu) plus de centrale-postgres generatievariant.1 parent 7774cf4 commit 6ff08cb
42 files changed
Lines changed: 3429 additions & 108 deletions
File tree
- docs
- features
- operations-manager/python
- opi
- api
- connectors
- core
- forms/editables
- handlers
- jobs
- manager
- backup
- services
- utils
- tests
- weekly
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
189 | 189 | | |
190 | 190 | | |
191 | 191 | | |
192 | | - | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
193 | 209 | | |
194 | 210 | | |
195 | 211 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
0 commit comments