Commit ce4e658
managing osmo configs with k8s configmap (#822)
* feat: add ConfigMap-sourced dynamic configuration loader
Add support for loading dynamic configs from a Kubernetes ConfigMap on
service startup, enabling GitOps workflows where configs are version-
controlled in Helm values and applied automatically via ArgoCD.
Two managed_by modes per config type:
- seed (default): only apply if config doesn't exist in DB
- configmap: always overwrite DB from ConfigMap on startup
Includes:
- configmap_loader.py: core loader with dependency ordering, advisory
lock for multi-replica safety, secret file resolution for dataset
credentials, and per-type error isolation
- dynamic_config_file field on WorkflowServiceConfig (CLI + env var)
- Helm chart: ConfigMap template, Secret template for credentials,
volume mounts, checksum annotation for pod restart on config change
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address bugs and code quality issues in configmap_loader
- Fix crash when managed_configs is None (empty YAML section)
- Fix partial state corruption in _resolve_dataset_secret_files by
validating secret file content before mutating credential dict
- Add configmap_loader.py and pyyaml dep to BUILD file
- Add type annotations for apply_function, model_class params
- Chain exception in _parse_managed_by (raise from error)
- Use RETURNING clause in _insert_backend to skip history entry on
conflict instead of always creating one
- Add schema reference comment to _insert_backend
- Warn on unknown keys in managed_configs
- Update copyright years to 2025-2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add comprehensive tests for ConfigMap config loader
Add unit tests (20 tests) and integration tests (18 tests) for the
configmap_loader module. Unit tests cover file handling, YAML parsing,
managed_by mode parsing, secret file resolution, safe_apply error
isolation, advisory lock behavior, and unknown key warnings.
Integration tests verify actual DB interactions including seed vs
configmap mode for all config types, dependency ordering, per-item
error isolation, end-to-end loading, and config history entries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review findings, add config watcher, fix test failures
- Advisory lock: session-scoped -> transaction-scoped (pg_try_advisory_xact_lock)
to prevent lock leaks on process kill
- Seed mode check: use config_history table instead of exclude_unset
to correctly detect if config was ever explicitly set
- Config watcher: background polling thread (30s default) with SHA-256
hash comparison detects ConfigMap file changes without pod restart
- Roles: pre-construct RolePolicy objects for pydantic v1 compatibility
- Dataset: auto-populate credential endpoint from bucket dataset_path
- Credential logging: removed secret file path from log messages
- Tests: fixed fetch_from_db return types, semantic action format,
advisory lock assertions, unused imports, mypy annotations
- E2E: added test values overlay and dynamic config test file
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* simplify: consolidate singleton config functions, fix advisory lock
- Consolidate _apply_service_config, _apply_workflow_config, _apply_dataset_config
into single _apply_singleton_config with config_type parameter and optional
pre_apply hook (dataset uses it for secret resolution)
- Fix advisory lock: revert to session-scoped pg_try_advisory_lock with explicit
unlock in finally block. Transaction-scoped xact lock was released immediately
by execute_fetch_command's auto-commit, providing zero mutual exclusion.
- Replace _backend_exists with _named_config_exists (eliminates duplicate)
- Use SELECT 1 LIMIT 1 instead of COUNT(*) in _singleton_config_exists
- Remove duplicate _DEFAULT_POLL_INTERVAL_SECONDS constant
- Add WHY comment for endpoint defaulting from dataset_path
- Update unit tests for session lock assertions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add drift reconciliation for configmap-mode configs
The ConfigMapWatcher now has two-tier polling:
1. File change detection (existing): SHA-256 hash comparison on the
mounted ConfigMap file. When the file changes, re-apply everything.
2. Drift reconciliation (new): for managed_by=configmap singleton
configs (service, workflow, dataset), compare the last-applied
ConfigMap values against current DB state on each poll cycle. If
someone modified a config via CLI, the watcher detects the drift
and re-applies the ConfigMap values within one poll interval.
Key design decisions:
- Only reconciles singleton configs (not named configs like pools)
to limit DB query load per cycle
- Compares desired values against DB BEFORE calling patch_configs,
so no config_history entries are created when there's no drift
- Uses a separate advisory lock (configmap-reconcile) to prevent
multiple replicas from correcting the same drift simultaneously
- Resolves secret_file references in the cached config so dataset
drift detection compares resolved credentials, not file paths
- Seed-mode configs are never reconciled (by design)
Also refactors module-level start_config_watcher + global state
into a ConfigMapWatcher class with clean instance state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add UX improvements — managed_by visibility, immediate re-apply, credentialSecretName
Improvement 1: Management mode visibility
- Config GET endpoints (service, workflow, dataset) include _managed_by field
in responses ('configmap', 'seed', or absent)
- Managed modes persisted to configmap_state table on startup
- get_managed_mode() and get_cached_section() exposed for cross-module access
Improvement 2: Immediate re-apply after CLI write
- When patch_configs writes to a configmap-managed singleton config, the
ConfigMap values are immediately re-applied (inside helpers.py)
- Only triggers for non-configmap-sync writers (prevents infinite loops)
- Eliminates the need to wait for 30s drift reconciliation poll
Improvement 3: Simplified secret wiring (credentialSecretName)
- Dataset buckets can now use credentialSecretName instead of secret_file
- Helm template auto-generates volume + volumeMount for referenced secrets
- Loader resolves credentialSecretName to /etc/osmo/secrets/<name>/cred.yaml
- No manual extraVolumes/extraVolumeMounts needed
Also adds configmap_state table to postgres.py and test schema.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: reject CLI writes to configmap-managed configs with 409 Conflict
Replace accept-then-revert pattern with 409 rejection for all config
types. When a user tries to modify a config managed by ConfigMap in
configmap mode, the API returns 409 Conflict with a clear error message
instead of silently reverting after the write.
Singleton configs (service, workflow, dataset):
- PUT/PATCH endpoints reject with 409 before any DB write
- Removed the accept-then-revert hook from helpers.py patch_configs()
Named configs (pools, pod_templates, backends, roles, etc.):
- 19 single-item PUT/PATCH/DELETE endpoints reject with 409
- Bulk PUT endpoints (put_pools, put_pod_templates, etc.) are NOT
guarded because the configmap_loader uses them internally
Benefits over accept-then-revert:
- Zero race conditions (no concurrent re-apply with watcher)
- Zero duplicate history entries (write never happens)
- Zero wasted side effects (no backend queue syncs, K8s updates)
- Clear API feedback ("update Helm values instead")
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: extract configmap_guard module, fix circular import, cover all write paths
Extract 409 guard logic into configmap_guard.py — a standalone module with
no imports from helpers.py or configmap_loader.py. This eliminates the
circular import (helpers -> configmap_loader -> helpers) that required
import-outside-toplevel violations.
Architecture:
- configmap_guard.py: holds managed config state, guard functions, constants
- configmap_loader.py: delegates guard functions to configmap_guard, pushes
state via set_managed_configs() on load_and_apply()
- helpers.py: imports configmap_guard at top level (no circular import)
- config_service.py: imports configmap_guard for _add_managed_by and named
item guards
Coverage:
- Singletons: guarded in helpers.put_configs() and helpers.patch_configs()
(covers all singletons including rollback)
- Named items: guarded in all 19 single-item endpoints + 6 bulk endpoints
+ rollback endpoint in config_service.py
- configmap-sync bypass: reject_if_managed() checks username and skips
for the loader's own writes
Also:
- Eliminated triple file read in load_and_apply() (reads once, computes
hash + parses YAML from same bytes)
- Removed dead get_configmap_state() from postgres.py
- Updated drift test to expect 409 rejection instead of accept-then-revert
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: remove debug print, dead code, stale comment
- Removed debug print(test_configs) from helpers.py
- Removed unused re-export delegations from configmap_loader.py
(get_managed_mode, get_cached_section, etc. — these are accessed
directly from configmap_guard, not via configmap_loader)
- Removed stale comment about circular imports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: rollback guard referenced removed configmap_loader.reject_if_managed
The rollback_config endpoint still called configmap_loader.reject_if_managed
which was removed when extracting to configmap_guard. Fixed to use
configmap_guard.reject_if_managed. Also removed unused configmap_loader
import from config_service.py.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add design doc for ConfigMap-sourced dynamic configuration
Covers architecture, management modes, 409 rejection, drift
reconciliation, credentialSecretName, and full E2E test results
from live dev instance validation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: guard watcher startup to avoid service boot abort
Wrap ConfigMapWatcher.start() in try/except so a malformed ConfigMap
file or DB error during initial config load doesn't prevent the service
from starting. The service logs the error and continues without
ConfigMap config management.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: extend secret file references to all config credential fields
Generalize the dataset-only _resolve_dataset_secret_files() into a
recursive _resolve_secret_file_references() that works for all config
types. Any dict with a 'secret_file' or 'secretName' key at any
nesting level gets resolved by reading the mounted K8s Secret file.
Supports two patterns:
- Dict secrets (credentials): file contents merged into the dict
- Simple string secrets (tokens, passwords): file with {value: "..."}
replaces the dict entirely with the string value
Helm templates updated:
- _helpers.tpl: recursive secretName→secret_file transformer +
secret name collector (4 levels deep)
- dynamic-config.yaml: resolves secretName in workflow/service config
- api-service.yaml: auto-generates volume + volumeMount for all
secretName references across all config types
- values.yaml: documents secretName pattern for workflow credentials
Example Helm values:
workflow:
config:
workflow_data:
credential:
secretName: osmo-workflow-data-cred
workflow_alerts:
slack_token:
secretName: osmo-slack-token
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: auto-detect Docker registry .dockerconfigjson secret format
The secret file resolver now auto-detects three formats:
1. Simple string: {value: "..."} → replaces dict with string
2. Docker registry: {auths: {registry: {username, auth}}} → extracts
registry, username, auth fields automatically
3. YAML dict: merges all keys into current dict (existing behavior)
Added secretKey field alongside secretName to specify which key in
the K8s Secret to mount (defaults to "cred.yaml"). For Docker
registry secrets, use secretKey: .dockerconfigjson.
Example:
backend_images:
credential:
secretName: imagepullsecret
secretKey: .dockerconfigjson
This lets users reuse existing imagepull secrets without creating
a duplicate secret in a different format.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: redesign ConfigMap config to in-memory + watchdog + global mode
Major redesign based on team feedback:
1. Single global toggle (no per-config managed_by seed/configmap)
- dynamicConfig.enabled: true = ConfigMap mode (all writes blocked)
- dynamicConfig.enabled: false = DB mode (CLI/API works normally)
2. In-memory config serving (standard K8s pattern)
- ConfigMap file parsed on startup, cached in module-level dict
- All config reads served from memory, not DB
- DB only for runtime state (agent heartbeats, k8s_uid) + roles
- Runtime fields (service_auth) injected from DB on first load
3. Watchdog file events replace 30s polling
- Uses watchdog library (already a dep) with inotify
- Watches parent dir for K8s ConfigMap symlink swaps
- 2s debounce for atomic swap events
4. All-or-nothing validation before applying
- Pydantic model validation for singleton configs
- Structure validation for named config sections
- On failure: previous config preserved, error logged
5. Simplified 409 write protection
- Single reject_if_configmap_mode(username) function
- 29 guard calls across config_service.py + helpers.py
- Bug fix: added missing delete_dataset guard
- Bug fix: simplified rollback guard
New: src/utils/configmap_state.py (dependency-free state module)
Modified: 11 files, ~900 lines added, ~2000 lines removed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: rewrite design doc for v2 architecture
Updated to reflect redesigned ConfigMap config system:
- Global mode (no per-config managed_by)
- In-memory serving (standard K8s pattern)
- Watchdog file events (no polling)
- Config vs runtime data separation
- All-or-nothing validation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: authz_sidecar reads roles from ConfigMap file instead of DB
Add --roles-file flag to authz_sidecar. When set, the sidecar reads
roles, external role mappings, and pool names from the ConfigMap-mounted
YAML file instead of PostgreSQL. This eliminates the DB dependency
from the authz_sidecar in ConfigMap mode.
New: src/utils/roles/file_loader.go
- FileRoleStore loads roles + external_roles from YAML
- In-memory reverse map: externalRole -> []osmoRole
- ResolveExternalRoles replaces SyncUserRoles SQL query
- Poll-based file watching (os.Stat every 30s)
Modified: authz_server.go
- NewFileBackedAuthzServer constructor (no pgClient needed)
- Check(): file-backed ResolveExternalRoles vs DB SyncUserRoles
- resolveRoles(): file store vs DB for role policy fetches
- computeAllowedPools(): file store vs DB for pool names
- MigrateRoles(): skipped in file-backed mode
Modified: main.go
- --roles-file flag for ConfigMap mode
- Conditional init: file-backed vs DB-backed server
- DB connection only created when needed
Modified: configmap_loader.py
- Removed _write_roles_to_db (authz_sidecar reads file directly)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: authz_sidecar uses --roles-file when dynamicConfig enabled
Helm template conditionally passes --roles-file to the authz-sidecar
container and mounts the dynamic-config ConfigMap volume. When
dynamicConfig.enabled=true, postgres args are omitted (not needed).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: move product defaults to chart values, auto-derive service_base_url
Chart values.yaml now ships with:
- Default workflow limits (max_num_tasks, timeouts)
- Default pod templates (ctrl, user)
- Default resource validations (cpu, memory)
- All 5 RBAC roles with policies (admin, user, default, ctrl, backend)
service_base_url auto-derived from services.service.hostname in the
dynamic-config template — no need to set it per deployment.
Per-deployment values only need site-specific configs:
- Dataset buckets + credentials
- Backend definitions
- Workflow image credentials
- CLI version info
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add default backend and pool to chart defaults
Matches configure_app() behavior: default backend with kai scheduler,
default pool referencing default backend with default platform.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update design doc for v3 — authz file-backed, chart defaults
- Authz sidecar reads roles from ConfigMap file (FileRoleStore)
- IDP groups as source of truth for user role assignments
- Product defaults (roles, templates, validations, backend, pool) in chart
- Per-deployment values only for site-specific overrides
- Updated architecture diagram, component table, E2E test results
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: eliminate remaining DB deps in ConfigMap mode
- Role.list_from_db/fetch_from_db now check snapshot (was missing)
- ConfigMapWatcher.postgres is optional (None when DB not available)
- _inject_runtime_fields skips DB read when postgres is None
- All config types now served from in-memory when ConfigMap mode active
Remaining DB access in ConfigMap mode is ONLY:
- Backend heartbeats (agent writes, pool status reads)
- configure_app() startup (runs before ConfigMap mode activates)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: fix medium review findings — remove stale docs and dead code
- Remove stale managed_by mode comments from values.yaml (v1/v2 leftover)
- Remove unused dynamic_config_poll_interval field from WorkflowServiceConfig
(Python watcher uses watchdog events, Go sidecar has its own constant)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: flatten ConfigMap YAML — remove managed_configs/config/items nesting
ConfigMap format now matches API response format directly:
- Removed `managed_configs:` top-level wrapper
- Removed `config:` sub-key from singleton types (service, workflow, dataset)
- Removed `items:` sub-key from named types (pools, roles, templates, etc.)
Before: snapshot['service']['config']['cli_config']
After: snapshot['service']['cli_config']
Before: snapshot['pools']['items']['default']
After: snapshot['pools']['default']
This means `osmo config show` output can be pasted directly into
Helm values with no format translation.
7 files changed: Helm template, Helm values, Python loader,
postgres.py (15 interceptions), Go file_loader, unit tests,
integration tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: clean up v1/v2 leftovers — remove dead code and stale references
- Delete unused set_configmap_state() method from postgres.py
- Remove dead CONFIGMAP_SYNC_USERNAME/TAGS imports from loader
(loader no longer writes to DB)
- Remove managed_by fields from example values (v3 uses global toggle)
- Flatten example values to match v3 structure (no config:/items:)
- Remove TestDynamicConfigSeedMode E2E test class (seed mode removed in v3)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: correct YAML indentation in chart values after items: removal
The children of removed items:/config: wrappers kept their extra
indentation, causing YAML parse errors in helm template.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update api-service.yaml for flat config structure
Remove .config. and .items. from dynamicConfig references in
api-service.yaml template (volume mounts for secrets).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add dynamic-config volume to agent and logger deployments
The authz-sidecar container mounts the dynamic-config ConfigMap for
file-backed role reading. The volume was only defined in api-service.yaml
but the sidecar is also included in agent and logger deployments.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add checksum annotation to agent/logger, remove duplicate guards
K8s/Helm review findings:
- Add checksum/dynamic-config annotation to agent and logger pods
so they restart on ConfigMap changes (was only on api-service)
- Remove duplicate {{- if }} guards in api-service.yaml secret
collection (copy-paste artifact)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: rename dynamicConfig → configs across codebase
Rename the feature from "dynamicConfig" to "configs" to match
industry conventions (ArgoCD uses "configs:").
Helm values: services.dynamicConfig → services.configs
ConfigMap name: osmo-service-dynamic-config → osmo-service-configs
Mount path: /etc/osmo/dynamic-config/ → /etc/osmo/configs/
CLI arg: --dynamic_config_file → --config_file
Env var: OSMO_DYNAMIC_CONFIG_FILE → OSMO_CONFIG_FILE
14 files, mechanical rename, no logic changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: replace recursive secret templates with explicit secretRefs
Pattern 2: secrets declared separately from config, not inline.
Before (80 lines of manually unrolled 4-level recursion):
- osmo.resolve-secret-names-in-config: walked config tree to transform
secretName → secret_file in the ConfigMap YAML
- osmo.collect-secret-names: walked config tree to find all secretName
references for volume mount generation
- dynamic-config-secrets.yaml: created K8s Secrets from inline values
After (simple flat list):
- secretRefs: list of {secretName} in values.yaml
- Template iterates list for volume + volumeMount generation
- secretName passed through to ConfigMap as-is
- Python loader resolves secretName → file path at runtime
- No inline secret creation (secrets managed out-of-band)
Deleted: dynamic-config-secrets.yaml, resolve-secret-names-in-config,
collect-secret-names, resolve-secret-name templates (~100 lines removed)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: rename dynamic-config files to configs
- dynamic-config.yaml → configs.yaml (Helm template)
- configmap-dynamic-config.md → configmap-configs.md (design doc)
- osmo_dynamic_config_values.yaml → osmo_configs_values.yaml (example)
- test_dynamic_config.py → test_configs.py (E2E test)
- Updated internal references in renamed files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: cleanup dead code and stale references from review
- Remove unused CONFIGMAP_SYNC_TAGS constant
- Remove unused typing imports from configmap_guard
- Fix integration test: remove stale managed_configs wrapper (#1)
- Fix docstrings: remove "write roles to DB", "authz reads from DB"
- Remove dead configmap_state table from postgres.py and schema.sql
- Remove stale comment about deleted secret templates in _helpers.tpl
- Rename $dc → $cfg in configs.yaml template
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: bypass caches in file-backed authz mode
In file-backed mode, FileRoleStore.GetRoles() and GetPoolNames() are
already in-memory map lookups. The LRU role cache and pool name cache
add overhead (locking, TTL) for zero benefit.
- resolveRoles: direct file store lookup, skip role cache
- computeAllowedPools: direct file store lookup, skip pool name cache
- Caches only used in DB mode where they avoid DB round-trips
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: optimize loader + remove dead code
configmap_loader.py:
- Remove unnecessary copy.deepcopy (yaml.safe_load returns fresh dict)
- Remove stale comments (circular import note, "kept from original")
- Fix remaining "Dynamic config" log message
- Remove extra blank lines
file_loader.go:
- Remove unused GetAllRoleNames() method (never called)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* perf: don't allocate caches in file-backed authz mode
roleCache and poolNameCache are unused in file-backed mode (all
lookups go directly to in-memory FileRoleStore). Don't create them.
- NewFileBackedAuthzServer no longer accepts cache params
- initFileBackedServer no longer creates caches
- Caches only allocated in DB mode where they serve a purpose
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review findings #1, #2, #3, #4, #9
#1 (CRITICAL): Store watcher on app state to prevent GC from
killing the file watcher after configure_app() returns.
#9: Activate ConfigMap mode on deferred successful reload.
If initial load fails but a later watchdog-triggered reload
succeeds, 409 write protection now activates correctly.
#3: Move reject_if_configmap_mode() before loops in 6 bulk
endpoints (put_pools, put_pod_templates, put_group_templates,
put_resource_validations, put_roles, put_backend_tests).
Guard doesn't depend on loop variable.
#4: Fix Go race on lastModTime in FileRoleStore. Move write
inside mu.Lock in Load(), add mu.RLock for read in Start().
#2: Fix import ordering in postgres.py — move configmap_state
to first-party import group.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: resolve CI failures after merge + add test coverage
Fixes:
- Remove PrettyJSONResponse (removed on main in pydantic v2 migration)
- Fix pylint: rename _config_watcher, narrow exception catch
- Fix mypy: use Any type for ResourceAssertion snapshot returns
- Fix assertion: compare scheduler_type enum .value not object
- Remove accidentally committed ui/node_modules file
- Remove run/minimal/osmo_configs_values.yaml (rework later)
Tests (14 new):
- 8 Go tests for file-backed authz (Check, resolveRoles,
computeAllowedPools, MigrateRoles, external role resolution)
- 6 Python tests for snapshot read paths (GroupTemplate,
Role, Backend list/names)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 1545336 commit ce4e658
27 files changed
Lines changed: 2976 additions & 103 deletions
File tree
- deployments/charts/service
- templates
- projects
- run
- e2e
- minimal
- src
- service
- authz_sidecar
- server
- core
- config
- tests
- workflow
- tests/common/database/testdata
- utils
- connectors
- roles
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
34 | 37 | | |
35 | 38 | | |
36 | 39 | | |
| |||
145 | 148 | | |
146 | 149 | | |
147 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
148 | 155 | | |
149 | 156 | | |
150 | 157 | | |
| |||
188 | 195 | | |
189 | 196 | | |
190 | 197 | | |
191 | | - | |
| 198 | + | |
192 | 199 | | |
193 | 200 | | |
194 | 201 | | |
195 | 202 | | |
196 | 203 | | |
197 | 204 | | |
198 | 205 | | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
199 | 216 | | |
200 | 217 | | |
201 | 218 | | |
| |||
247 | 264 | | |
248 | 265 | | |
249 | 266 | | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
250 | 277 | | |
251 | 278 | | |
252 | 279 | | |
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
453 | 453 | | |
454 | 454 | | |
455 | 455 | | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
456 | 459 | | |
457 | 460 | | |
458 | 461 | | |
| |||
461 | 464 | | |
462 | 465 | | |
463 | 466 | | |
| 467 | + | |
464 | 468 | | |
465 | 469 | | |
466 | 470 | | |
| |||
483 | 487 | | |
484 | 488 | | |
485 | 489 | | |
486 | | - | |
487 | 490 | | |
488 | 491 | | |
489 | 492 | | |
490 | 493 | | |
491 | 494 | | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
492 | 500 | | |
493 | 501 | | |
494 | 502 | | |
495 | | - | |
496 | 503 | | |
497 | 504 | | |
498 | 505 | | |
| |||
508 | 515 | | |
509 | 516 | | |
510 | 517 | | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
511 | 523 | | |
512 | 524 | | |
513 | 525 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
229 | 229 | | |
230 | 230 | | |
231 | 231 | | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
232 | 418 | | |
233 | 419 | | |
234 | 420 | | |
| |||
0 commit comments