feat: config overrides for structured config files#1177
Open
feat: config overrides for structured config files#1177
Conversation
…nto feat/config-overrides-json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Implementation of https://github.com/stackabletech/decisions/issues/73, needed for stackabletech/opa-operator#756
Problem
The existing
configOverridesmechanism usesHashMap<String, HashMap<String, String>>(filename → flat key-value pairs). This works well for flat formats like.propertiesfiles and Hadoop XML, but it cannot express modifications to nested or structured formats like JSON. For example, there is no clean way to overridemin_delay_secondsinside a deeply nested OPAconfig.json.Solution
This PR adds strategy-based
configOverridesbuilding blocks tooperator-rs. Instead of a single flat map, operators can now compose typed override structs that choose a patch strategy per config file. The CRD schema explicitly encodes which strategies are supported for which files, which means invalid input is rejected by the Kubernetes API server before the operator ever sees it.The general architecture for config overrides is: operator-rs handles merging (combining base config with user overrides), while the operator handles rendering (turning the merged result into the actual file content). For key-value files, the merging happens inside the existing product config pipeline. For structured files like JSON, the merging happens via e.g.
JsonConfigOverrides::apply(). Both are operator-rs code. For rendering, the operator picks the right format: sometimes using shared helpers from operator-rs (e.g. properties file writers), sometimes doing it directly (e.g.serde_json::to_string_pretty).Changes to
CommonConfiguration,Role, andRoleGroupThese structs gained a new
ConfigOverridestype parameter. It defaults toHashMap<String, HashMap<String, String>>, so all existing operators compile and behave identically without any changes. Operators that want to opt in to structured overrides specify their own type (e.g.OpaConfigOverrides) as the fourth generic parameter.New
config_overridesmoduleIt contains:
New patch strategies for JSON files:
JsonConfigOverrides: enum supporting three strategies for JSON config files:jsonMergePatch: RFC 7396, simple nested overrides expressed as YAML/JSONjsonPatches: RFC 6902, fine-grained operations (add, remove, replace, move, test)userProvided: full file replacement escape hatchTyped key-value overrides:
KeyValueConfigOverrides: typed wrapper for flat key-value files (.properties, Hadoop XML).Uses
#[serde(flatten)]so it serializes identically to the oldHashMap<String, String>.KeyValueOverridesProvidertrait:configOverridestype. The default implementation returns an empty map, so operators that only use structured overrides (like OPA) don't need any custom logic. It is implemented forHashMap<String, HashMap<String, String>>, to be fully backwards compatible.The shared product config pipeline (
transform_all_roles_to_config) processesPropertyNameKind::Fileentries by callingget_key_value_overrideson theConfigOverridestype. Rust requires this trait bound at compile time even if a particular operator never passesPropertyNameKind::Fileentries. Operators that only use structured overrides (like OPA withJsonConfigOverrides) can rely on the default no-op implementation. I don't think we can get around theimpl KeyValueOverridesProvider for OpaConfigOverrides {}line in operators that use the new type config overrides, that's the small price of making the solution backwards compatible while still having the exact types in the CRD for operators that use typed config overrides.Example for a hypothetical NiFi with the new typed overrides, which uses
KeyValueConfigOverrides:Why a generic type parameter instead of an enum?
An enum containing all strategies would mean every operator advertises support for every strategy on every file. The current approach (operators compose a struct from building blocks) lets the CRD schema precisely reflect what is actually supported. Invalid combinations are rejected at admission time, not at runtime.
What is NOT included
XmlConfigOverrides/ XML patch strategy (RFC 5261): will be added when needed (e.g. for NiFi).Definition of Done Checklist
Author
Reviewer
Acceptance