Repository navigation
Support for key aliases - #70
Merged
Merged
Conversation
This also fixes a small bug and introduces a single string `alias` key in addition to a list of multiple `aliases`: the single key version is very likely to be used substantially more often.
When `$(old.key)` is used in a RHS substitution and `old.key` is a deprecated alias that has been rewritten to its canonical key, the previous error message said only that the config variable was not set, with no hint that the key was a known alias. Enrich the error in `map_value_sub/3` by post-processing the error list: any `substitution_missing_config` error whose missing variable is a known alias is rewritten to `substitution_alias_key`, which names both the alias and the canonical key it maps to. Add `xlate/1` clause for `substitution_alias_key` and a test that asserts the formatted message mentions "alias".
"Resolving Aliases" was inconsistent with the surrounding log messages. Change to "Resolving aliases".
lukebakken
approved these changes
Apr 12, 2026
lukebakken
left a comment
Collaborator
There was a problem hiding this comment.
Feel free to ship 3.7 if you'd like, thanks!
This was referenced Apr 12, 2026
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.
This implements the design in #69 with a few deviations or additions:
aliasis a single string key property accepted as a common alternative toaliases, an array of strings{aliases, []}in a merge mapping is a deliberate "clear all aliases" directive - it clears aliases inherited from the base mapping. This is intentionally asymmetric withseeanddoc, where an empty list in a merge mapping means "not specified, inherit".RHS Substitution Limitation
Alias keys are rewritten to their canonical keys early in the pipeline, before RHS substitutions are evaluated. This means
$(old.key)in a substitution will fail ifold.keyis an alias - use$(new.key)instead. Cuttlefish will emit a specific error message identifying the alias and naming the canonical key to use.A Realistic Example
In this hypothetical (for now anyway) scenario, assume that Team RabbitMQ
wants to move a few
rabbitmq.confkeys under theraft.quorum_queue.*namespace (raft.{subsystem}.{key}, if we generalize).Today this requires duplicating the mapping and writing boilerplate translations
for every key, and making sure you get it right every time:
{mapping, "raft.quorum_queue.segment_max_entries", "ra.segment_max_entries", [ {datatype, integer}, {validators, ["non_zero_positive_integer", "positive_16_bit_unsigned_integer"]} ]}. {mapping, "raft.segment_max_entries", "ra.segment_max_entries", [ {datatype, integer} ]}. {translation, "ra.segment_max_entries", fun(Conf) -> case cuttlefish:conf_get("raft.quorum_queue.segment_max_entries", Conf, undefined) of undefined -> cuttlefish:conf_get("raft.segment_max_entries", Conf, cuttlefish:unset()); Value -> Value end end}.With aliases, the old keys just work and the implementation is dramatically less error-prone:
{mapping, "raft.quorum_queue.segment_max_entries", "ra.segment_max_entries", [ {datatype, integer}, {validators, ["non_zero_positive_integer", "positive_16_bit_unsigned_integer"]}, %% a shortcut for {aliases, ["raft.wal_max_size_bytes"]} {alias, "raft.wal_max_size_bytes"} ]}.A Basic Conflict Resolution Example
If both the canonical key and the alias are set, the canonical key takes precedence:
{mapping, "raft.quorum_queue.wal_max_size_bytes", "ra.wal_max_size_bytes", [ {datatype, integer}, {validators, ["non_zero_positive_integer"]}, {alias, "raft.wal_max_size_bytes"} ]}.The outcome: the value of 536870912 will be used, and Cuttlefish will emit a warning:
Other Comments
Closes #69.