Skip to content

Meta: validated freeform service configuration — key ownership, per-release option catalog, ControlPlane passthrough #701

Description

@berendt

Category: feature | Scope: Large

Description

The service operators accept freeform configuration through an extraConfig field on the Keystone, Glance, and Horizon custom resources. Whatever a user puts there wins the render-time merge against every operator-computed default, and nothing checks it. A misspelled option name reaches the rendered configuration file unchanged and surfaces only as a crash-looping service pod. An option the operator itself depends on can be overwritten with no warning at all.

This Meta Issue covers three work packages that make that field trustworthy, without turning individual configuration options into typed CRD fields.

The first package (#702) makes key ownership explicit. Some options are not the user's to set: either the operator computes them, or a typed field on the same resource already owns them and an override would silently discard it. A few such guards exist today and they are inconsistent — one rejects at admission, another accepts the value and reports it through a status condition and a warning event, and most operator-managed options have no guard at all. This package introduces one registry of operator-owned keys per service and applies it uniformly. The guard reports rather than rejects: the value is honored, a status condition turns false, and a warning event names the key. Rejecting at admission would also fire on updates to unrelated fields of an existing resource, so tightening it later is a separate decision and not part of this work. One case keeps its current hard rejection — where a typed field owns the key and the override would drop the typed value silently. This package also settles the merge precedence, which today differs between services: for one, plugin-supplied sections beat operator defaults; for another, the reverse. One documented precedence chain replaces both.

The second package (#703) validates option names against the options the deployed service actually has. forge builds its own service images per OpenStack release, so the catalog can be extracted from the shipped image rather than from a pinned copy of the service's source, and it stays tied to the release it describes. Validation runs when the resource is submitted rather than when it is reconciled, so a typo is rejected by kubectl apply with the offending section and key named. Sections contributed through the plugin mechanism are exempt, because a driver registers its options only once it is loaded and the catalog cannot see them. This applies to Keystone and Glance. Horizon's freeform field carries Django settings rather than INI, and those have no machine-readable option registry, so Horizon gets the ownership registry from the first package and no catalog.

The third package (#704) makes service configuration reachable from the ControlPlane. Today it is not: the ControlPlane exposes a curated subset of each service's settings and no freeform configuration at all, and editing the projected child resource directly does not survive, because the operator reverts it. A platform operator running the ControlPlane through GitOps therefore has no path to any service configuration option. This package adds a global block and a per-service block, merged the way policy overrides already are, with the result projected onto the child. Both checks from the earlier packages run against the merged result, and the message names which of the two levels contributed the offending key. Whether a per-service block replaces a whole section or merges into it key by key is decided and documented here rather than left to follow from the merge helper that happens to be used.

The validation is one module shared by the service operators and the ControlPlane, rather than logic embedded in a single operator's admission path. The third package depends on that. Building it any other way means a bad ControlPlane resource is accepted on submission and fails later during projection, which is the failure mode this work exists to remove.

Motivation

A platform operator deploying through the ControlPlane cannot set a service configuration option at all. The ControlPlane offers no freeform block, and the projected child resource is reverted on the next reconcile. The only remaining route is a change to forge itself, which puts a release cycle between an operator and a one-line configuration change.

Where the field is reachable, it is unguarded, and the failures are quiet. Overriding the fernet key limit is the clearest case: the operator also uses that value to decide how many keys to project into the pod, so an override leaves the configuration file and the mounted key material disagreeing, with both the resource and the pod reporting healthy. Overriding an option that a typed field owns discards the typed value with nothing said. A misspelled option name is not caught anywhere between submission and a crash-looping pod.

Validation and ownership have to arrive together. They answer different questions — whether an option exists, and whether the user may set it — and neither covers the other. A catalog on its own would accept an operator-owned option, because such options are perfectly real ones, and would return a clean result for a change that breaks the deployment. That is worse than today's silence, because it carries an endorsement.

The comparable projects each stop short of this. Two ship a freeform escape hatch with no key-level protection, and one of those carries an unresolved note in its own source acknowledging the gap. The third validates thoroughly, but against a schema built from a pinned copy of the service's source rather than from the image it deploys, and it reports failures during reconciliation instead of on submission. Building the catalog from the shipped image and checking it at admission avoids both problems.


Drafted by planwerk-agent with Claude

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions