feat(excititor): persisted provider configuration + blocked-readiness (EXCITITOR-CFG-01/02/03)

Closes 3 of 4 tasks in SPRINT_20260422_007. EXCITITOR-CFG-04 (OCI
binary-material handling) stays BLOCKED pending a secret-reference
storage-model design decision — sprint header called that out as a
scope boundary.

Mirrors the SRC-CREDS pattern (commits 838257245 + earlier) to give
Excititor VEX providers the same persisted-credentials + blocked-
readiness contract that advisory sources now have.

Persistence (EXCITITOR-CFG-01):
- New vex.provider_settings table via embedded migration
  007_vex_provider_settings.sql (auto-applied by AddStartupMigrations).
  Key: provider_id; columns: settings jsonb, updated_by, timestamps.
- PostgresVexProviderSettingsStore (Dapper) + ProviderSettingsRow EfCore
  model + InMemoryVexProviderSettingsStore for tests.
- IVexProviderSettingsStore + VexProviderSettingsRecord added to
  StellaOps.Excititor.Core/Storage.
- Existing vex.providers row (trust, discovery, base_uris, enabled)
  untouched — additive only.

API surface:
- GET /excititor/providers/{id}/configuration → masked snapshot with
  fields: key, label, inputType, sensitive, required, value, hasValue,
  isSecretRetained, helpText, placeholder. Plaintext secrets never
  returned.
- PUT /excititor/providers/{id}/configuration with { values, clearKeys }.
  Sensitive fields submitted blank are retained; clearKeys explicitly
  deletes.
- Field schemas shipped for excititor:cisco / msrc / suse-rancher.

Effective settings + readiness (EXCITITOR-CFG-02):
- VexProviderConfigurationService.ComputeConfigurationFailure drives
  readiness. When persisted-enabled but missing required fields, the
  provider status reports blockingReasonCode=PROVIDER_CONFIG_REQUIRED
  (or PROVIDER_CONFIG_INVALID on validation failure), readiness=blocked.
  Configuration failures take priority over retry-backoff reasons so the
  actionable message surfaces first.
- VexIngestOrchestrator.ValidateConnectorAsync + ExecuteRunAsync resolve
  effective settings from VexProviderRuntimeSettingsCache; same
  settings flow into DefaultVexProviderRunner (worker scheduled runs).
  Previously those paths validated against empty / schedule-only options.

CLI + Web (EXCITITOR-CFG-03):
- CLI: `stella vex providers configure <provider> [--set k=v] [--clear k]
  [--format text|json]`. Aliases cisco/msrc/rancher → excititor:*.
- Web: VexProviderManagementApi.getConfiguration / updateConfiguration
  +VexProviderConfigurationComponent (Angular standalone). Component
  renders masked-secret + clear toggles + required indicators + help/
  placeholder. Routing intentionally minimal (no new route added) to
  avoid stepping on the parallel FE test agent.

Tests: targeted xUnit via scripts/test-targeted-xunit.ps1:
- VexProviderConfigurationServiceTests → Total: 8, Failed: 0
- ProviderManagementEndpointsTests regression → Total: 5, Failed: 0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-04-22 23:44:21 +03:00
parent dd98d6c3f6
commit 7efa424fe2
25 changed files with 2228 additions and 28 deletions

View File

@@ -21,18 +21,31 @@ Backend API:
- `POST /excititor/providers/{providerId}/enable`
- `POST /excititor/providers/{providerId}/disable`
- `POST /excititor/providers/{providerId}/run`
- `GET /excititor/providers/{providerId}/configuration` — persisted connector settings (masked secrets)
- `PUT /excititor/providers/{providerId}/configuration``{ values, clearKeys }` request shape, retains secrets submitted blank
Persisted connector configuration (Sprint `20260422_007`) is the primary operator path for credentialed VEX providers. Host-config and environment binding remain compatibility fallbacks only. Persisted settings win when both define the same key.
## Readiness states
Excititor providers use four runtime readiness states:
- `ready`: persisted-enabled and the current host has a runnable connector
- `blocked`: persisted-enabled but currently cooling down or otherwise not runnable
- `blocked`: persisted-enabled but missing required persisted configuration, cooling down, or otherwise not runnable
- `disabled`: persisted-disabled
- `planned`: cataloged provider without a runnable connector registered on the current host
`enabled` remains the operator intent flag. A provider can be `enabled=true` and still show `planned` or `blocked`.
### Blocked-readiness configuration codes (Sprint `20260422_007`)
When a persisted-enabled provider lacks required settings or fails connector-option validation, the blocked-reason surface carries one of:
- `PROVIDER_CONFIG_REQUIRED` — one or more required fields (e.g. MSRC `tenantId`, `clientId`, `clientSecret`) are absent.
- `PROVIDER_CONFIG_INVALID` — settings are present but the connector's own option validator rejected them (e.g. Cisco `metadataUri` is not an absolute URI, Rancher hub credential set is partial).
These codes mirror the Concelier `SOURCE_CONFIG_REQUIRED` / `SOURCE_CONFIG_INVALID` contract from SRC-CREDS-005 so CLI and Web surfaces can reuse the same rendering. The `/excititor/providers` list response exposes them via `blockingReasonCode` and `blockingReason`.
## Provider inventory
| Provider ID | Kind | Default enabled | Registered in WebService | UI control plane | CLI control plane | Credential / config status | Notes |
@@ -62,13 +75,29 @@ The current `update-provider` surface persists:
The current provider control plane does not yet persist connector-secret fields such as:
- MSRC tenant, client ID, client secret, static access token, or offline token path
- Rancher Hub token endpoint, client ID, or client secret
- OCI registry username, password, identity token, refresh token, cosign key pair, or image subscription list
- Cisco optional VEX API token
Those settings still come from host configuration today. The control plane is truthful about the gap by surfacing `planned` or `blocked` readiness instead of pretending the connector is runnable.
### Persisted scalar-provider configuration (Sprint `20260422_007`)
The following provider connector settings can now be persisted through the UI or CLI and are kept in a dedicated `vex.provider_settings` row (distinct from `vex.providers` metadata):
| Provider | Persisted fields |
| --- | --- |
| `excititor:cisco` | `metadataUri` (optional override), `apiToken` (sensitive) |
| `excititor:suse-rancher` | `discoveryUri`, `tokenEndpoint`, `clientId`, `clientSecret` (sensitive), `audience` |
| `excititor:msrc` | `tenantId`, `clientId`, `clientSecret` (sensitive), `scope` (optional override) |
Operator surfaces:
- CLI: `stella vex providers configure <provider> [--set key=value ...] [--clear key ...] [--format text|json]`
- Web: provider configuration panel rendering masked secret state, required-field markers, and explicit clear toggles.
Sensitive values are never re-exposed on reads — the API returns only `hasValue`/`isSecretRetained` flags. Blank submissions retain existing secrets; explicit `clearKeys` entries delete them.
OCI OpenVEX remains on host-config only pending the dedicated artifact-backed configuration path (`EXCITITOR-CFG-04`).
## Host wiring notes
- `StellaOps.Excititor.WebService` always registers: