-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generic deepmerge()
function
#31815
Comments
Thanks for gathering these requirements from the issues. |
I'd expect Example documentation:
|
I don't understand the statement, so I'm not sure what might need changing. |
I'm saying that |
Thanks for the clarification. I think you cannot have same interface as merge AND support multiple ways of deep merging, without resorting to multiple deepmerge functions. Alternatively, could a deepmerge function check its first argument (a map) for a certain structure, like the presence of only one key, called "options", and if the map fits then the function uses it to choose the merge behavior and applies merge to remainder of map args. The docs would still be straightforward addition to what you wrote. Eg "... If the first arg is a map that satisfies certain conditions (see section XYZ for description), it will be used to control the merge of list/map/object elements of the map args, instead of being merged with the other map args." |
The first argument could be a list of merge options and then still support every input that |
That works, although using a map has several advantages over a list:
It would be nice to not have to specify the options at all, and to be able to only specify those that need to be. |
Hey peeps, any updates over here? |
Ping! |
This would likely need to be implemented as a provider (external) function, if or when that becomes a possibility. Please see: #27696. |
There is a provider as a workaround used by this submodule https://registry.terraform.io/modules/cloudposse/config/yaml/0.2.0/submodules/deepmerge and without a provider using this module https://registry.terraform.io/modules/Invicton-Labs/deepmerge/null/latest The intent of ticket is to use a native function to avoid these workarounds |
Thank you for your continued interest in this issue. Terraform version 1.8 launches with support of provider-defined functions. It is now possible to implement your own functions! We would love to see this implemented as a provider-defined function. Please see the provider-defined functions documentation to learn how to implement functions in your providers. If you are new to provider development, learn how to create a new provider with the Terraform Plugin Framework. If you have any questions, please visit the Terraform Plugin Development category in our official forum. We hope this feature unblocks future function development and provides more flexibility for the Terraform community. Thank you for your continued support of Terraform! |
It is great to see the possibility to implement this via provider functions and I really believe these can be useful for provider specific tasks but recursive merge is a generic problem and it seems like that hashicorp just outsources the problem to the providers. Creating providers with a specific resource to solve such a problem was possible before and has been done by people, the problem is that this open the door for non standard implementations for the same function/task. Deep merge is not an obscure function and people wanting this since TF v0.12.16 (2020 #24987) with 129 upvotes since locked. Having a great standard library that solves a common problems that everybody has is impotent, failing this results in so much effort and 100 different implementations from each provider that sort of do the same - except when they dont. In the extrem the results can be seen in the javerscript world. Sad to see that the built in functions in terraform are kind of limited and dont get the attention they deserve. |
I actually expect that either Terraform or OpenTofu (or both) will make some of the built-in functions be syntactic sugar for functions from a built-in provider. For example, |
I've successfully drafted a |
To be fair, ~95% of Terraforms built-in functions are already just calls to the function library in go-cty1 (including the existing |
Nice! Would it make sense for your provider to be slightly higher level, eg "tree" and provide functions related to tree structures, like deepmerge() would be the first function. Community could contribute more, making "tree" the go-to provider for all tree related processing. Otherwise we could end up with a pile of tiny providers, but maybe that's not such a bad thing either. |
This seems like the correct answer to this problem. It would help standardize a deepmerge function instead of relying on providers to implement it "correctly". |
I've published an initial implementation with Code is at https://github.com/isometry/terraform-provider-deepmerge, contributions welcome!
output "test" {
value = provider::deepmerge::mergo(local.map1, local.map2)
} An arbitrary number of arbitrarily deep maps are theoretically supported, with similar merge logic to the built-in |
Well… you tried. If you don't like the |
@isometry Yeah... |
Hi all, As discussed over in the earlier issue on this subject #24987 (comment), we chose to encourage implementing this in external providers rather than as a builtin because there seems to be no strong consensus for what exactly the "deep merge" operation implies, and so it seems that multiple implementations is both inevitable and desirable to meet the different use-cases that led to those differing definitions of the function in other languages. I see that the Since there is now at least one provider offering an answer to this, I'm going to close this issue. Thanks! |
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. |
Terraform Version
Use Cases
Merging of files read via jsondecode and yamldecode, merge of nested maps and objects tree typically used for configuration or lookups
Attempted Solutions
merge()
: does not recursively merge values that are objects and mapsKyle Kotowik's
deepmerge
module: is computationally intensive (since the tree must be flattened, merged, then re-created) and is currently not usable ifinfracost
is used for PR costingProvider: not attempted yet but it is a fallback solution if this proposal is not accepted, because more work to create and maintain than the proposed builtin, and more work to use (since will have to add a provider, define data source etc)
Proposal
The
deepmerge(list_of_items_to_merge, merge_config})
would behave like this:Takes a list of items to merge
[item1, item2, ...]
, and an optional merge-configuration object{list_merge_strategy="replace", type_mismatch_strategy="complex_wins"}
, and returns a single item that is a merge of all items, based on the merge configuration.The merge algorithm for
list_of_items_to_merge
is as follows:type_mismatch_strategy
:-
complex_wins
: object and map win over lists which win over literals; the losers are removed from the merge;-
abort
: abort the terraform plan or applylist_merge_strategy
specified (which defaults to "replace") is applied:-
replace
: the list of the right most argument applies-
concat
: the sequence of lists is concatenated in the same order as arguments-
merge
: each list is appended withnull
so they all have the same length, then thedeepmerge()
is applied to each slice through the listsdeepmerge()
is applied to the sequence of associated values across all the itemsThis is backwards compatible with all versions of terraform, and should cover 99% of use cases.
Examples:
deepmerge([a, b, c, d])
:list_merge_strategy
list_merge_strategy
deepmerge()
is called on the corresponding list of associated values, and objects that don't have the key are not included. So ifa
haskey1
andkey2
, andb
haskey1
andkey3
, then the set of keys of the result is(key1, key2, key3)
and there will be 3 calls todeepmerge()
:deepmerge([a.key1, b.key1])
,deepmerge([a.key2])
, anddeepmerge([b.key3])
(the merge-config is passed to these but left out here for simplicity)References
Many threads of discussion about this on community forum and github issues related to
merge()
. Also, the table in #24987 (comment).The text was updated successfully, but these errors were encountered: