Skip to content

Conversation

@VihasMakwana
Copy link
Contributor

@VihasMakwana VihasMakwana commented Jun 24, 2025

Description

This RFC is a follow-up of #12097. The first PR introduced the feature gate to merge the components' lists and left out the options to configure the merging behaviour.

This RFC proposes an approach to extend the current behaviour by enabling merging of specified config parts and support different modes.

Link to tracking issue

Relates:

Thanks to @mx-psi @dmitryax and @evan-bradley for their feedback on the first PR!!

@codecov
Copy link

codecov bot commented Jun 24, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.38%. Comparing base (3bcdf80) to head (21caaf8).
⚠️ Report is 323 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #13256      +/-   ##
==========================================
- Coverage   91.62%   91.38%   -0.25%     
==========================================
  Files         522      646     +124     
  Lines       29208    42606   +13398     
==========================================
+ Hits        26763    38937   +12174     
- Misses       1926     2844     +918     
- Partials      519      825     +306     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@douglascamata douglascamata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff, @VihasMakwana. I like this. I left comments but they are non-blockers to me.

- In this example, we will merge the list of extensions and receivers from pipeline, excluding lists in the rest of the config.
- `otelcol --config main.yaml --config ext.yaml?merge_paths=service::extensions --config rec.yaml?merge_paths=service::**::receivers`
- In this example, we will merge all list of extensions from `ext.yml` and list of receivers from `rec.yaml`, excluding lists in the rest of the config.
2. `merge_mode`: One of `prepend` or `append`.
Copy link
Member

@dmitryax dmitryax Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need more options. For example, if I want to add a receiver in a merging config while it may be already defined in the base config. Also, we need the default replace behavior. What about the following list:

  • append: append entries to the list
  • append_unique: append only unique entries to the list
  • prepend: insert entries in front of the list
  • prepend_unique: insert only unique entries in front of the list
    replace (default): overwrite the list
    keep: keep the original list

Also, I would call the options list_merge_mode and list_merge_paths to make it clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmitryax I see. I'll update the RFC

Copy link
Member

@douglascamata douglascamata Jun 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you touch a good point there with the _unique modifiers, @dmitryax. Shouldn't both append and prepend always ensure lists containing only "basic types" (strings, ints, etc) do not have duplicated values? I don't know about any scenario in the Collector involving lists of basic types where duplicated values wouldn't be a problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For service components, duplicates will cause error.
I'm not sure about other places though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For service components, duplicates will cause error.

Right. For example, I have a list of receivers in a basic config file which I don't control. Then, I want to add another receiver and override it in case it's already defined in the basic config. In that case --config=extra_receiver.yaml?merge_mode=append&merge_paths=service::**::receivers would cause a failure if the basic config already has my receiver. I need append_unique instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can start with a small number of options, in any case we can discuss that in the implementation phase

1. `merge_paths`: A comma-separated list of glob patterns which will be used while config merging
- This setting will control the paths user wants to merge from the given config.
- Example:
- `otelcol --config main.yaml --config extra.yaml?merge_paths=service::extensions,service::**::receivers`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using the query params is fine. However, we need to keep in mind that some confmap providers use the same pattern to pass extra information. With this approach, they won't be able to use these keys which is probably fine.

If anyone else have any other ideas, please share.

cc @open-telemetry/collector-approvers

Copy link
Contributor Author

@VihasMakwana VihasMakwana Jun 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, we can extract the parameters here

for _, uri := range mr.uris {
and process them for our merge function.

Then we can remove our parameters from URI and let resolver do its job:

u, _ := url.Parse("otel.yaml?key=val&merge_mode=append")
q := u.Query()
q.Del("merge_mode")
u.RawQuery = q.Encode()
// uri will now be otel.yaml?key=val&key2=val2

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this in this RFC and came up with a few issues with query parameters that we'll want to consider before going that route.

@dmitryax I believe you, @mx-psi, and I had an offline conversation at KubeCon and tacitly agreed we should go the route of configuring these inside a config file (this option in the RFC). Do you still think we should go this route? I think we should probably at least explore it before deciding on an approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evan-bradley @dmitryax approach 1 does not deal with params, does this resolve your concerns here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't mind calling out the "Configuring Confmap Providers" RFC just to make it clear there are other options we can explore, but if approach 1 is our preference, I'm good with what's here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's fair

@mx-psi
Copy link
Member

mx-psi commented Jun 27, 2025

I haven't had time to look into this proposal in detail but one thing I would like to see explored is whether we can use YAML custom tags https://yaml.org/spec/1.2.2/#tags to specify this in the YAML itself per-array

@VihasMakwana
Copy link
Contributor Author

@mx-psi Thanks for sharing your thoughts! I'll surely go the documentation and see if they can be integrated. I'm all in if we can get it work through just yaml.

@VihasMakwana
Copy link
Contributor Author

VihasMakwana commented Jul 1, 2025

@mx-psi I had a brief look at yaml tags and their support in golang. Here are my thoughts:

  1. We rely on koanf for configuration management and it deals with map[string]any. It does not support the concept of yaml tags. To implement this feature using yaml tags, we need to extract tags somewhere around here
    // NewRetrievedFromYAML returns a new Retrieved instance that contains the deserialized data from the yaml bytes.
    // * yamlBytes the yaml bytes that will be deserialized.
    // * opts specifies options associated with this Retrieved value, such as CloseFunc.
    func NewRetrievedFromYAML(yamlBytes []byte, opts ...RetrievedOption) (*Retrieved, error) {
    var rawConf any
    if err := yaml.Unmarshal(yamlBytes, &rawConf); err != nil {
  • One thing to note: We use https://pkg.go.dev/sigs.k8s.io/yaml, which is a fork of gopkg.in/yaml.v3. I believe it should support yaml tags. I'll get back to you if I face any blockers.
  • Pseudo code for extracting tags:
var node Node
err := yaml.Unmarshal(data, &node)

// recursively go through the node and its children to find the nodes with tags and store their path.
  1. I still need to create a proof of concept to be sure it works. I'm planning to update the RFC with both approaches and decide on the best one after evaluating the pros and cons of each.

Here are some initial pros and cons that come to mind:

  1. Pros of yaml tag approach:

    • Straightforward from end-user's perspective. No need to extra CLI flags and everything can be specified in configuration files.
  2. Cons of yaml tag approach:

    • More preprocessing:
      • As koanf converts the given config into map[string]any, we need to do the preprocessing of tags before converting it to a koanf instance or else we'll lose the tags.
      • We need to go down the yaml tree to extract nodes with tags.

Let me know your thoughts!

@VihasMakwana
Copy link
Contributor Author

@mx-psi I just saw your raised #13308. That makes things a lot easier for yaml tags. Thanks!

github-merge-queue bot pushed a commit that referenced this pull request Jul 8, 2025
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

Promotes @VihasMakwana as triager. @VihasMakwana is a contrib triager
and has been doing excellent work in core, including pushing for RFCs
and features such as #13256, #13224 and #11775. Thank you for your work
@VihasMakwana !

cc @open-telemetry/collector-approvers
@github-actions
Copy link
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.


## Proposed approaches:

### Approach 1 (Recommended): Use yaml tags
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I discussed this over Slack with Vihas but mentioning it here: it would be good to make sure that our officially released Helm charts and operator do not break these YAML tags (they shouldn't but I think it's a good check to do before deciding)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've reached out on #otel-helm on CNCF slack for more eyes on this. I'll run some tests on this and keep you updated.

Copy link
Contributor Author

@VihasMakwana VihasMakwana Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mx-psi Unfortunately, YAML tags are not preserved in helm chart. I think this is due to the fact that our helm chart deals with yaml to dict conversion internally and tags are lost in this process.

The same applies to the operator. The operator marshals the config into a struct and internally, it is map[string]interface{}. Hence, the tags are lost in the conversion.

That being said, do users need this feature in helm/operator? Our helm chart internally deals with all the merging and appending to the lists, when necessary.

For operator, users deploy a single configuration like following:

kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: simplest
spec:
  config:
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318

    exporters:
      debug: {}

    service:
      pipelines:
        traces:
          receivers: [otlp]
          exporters: [debug]
EOF

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^ @mx-psi

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From 2025-09-10 Collector SIG meeting it seems like this should not be a concern but let's wait a bit more for the Operator SIG to answe

@VihasMakwana
Copy link
Contributor Author

cc: @evan-bradley @dmitryax

if you guys are around, please take a look at this RFC. I've documented both the approaches.

Here's a PoC for yaml tags: #13551

@VihasMakwana VihasMakwana requested a review from mx-psi August 20, 2025 10:51
@VihasMakwana
Copy link
Contributor Author

@mx-psi if you're around, can you please take a look?

Copy link
Member

@mx-psi mx-psi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's wait to see what the Operator SIG says but I think the suggested approach works and would be my preferred alternative

@mx-psi
Copy link
Member

mx-psi commented Sep 10, 2025

@dmitryax @evan-bradley could you take another look so we can know if there are any outstanding discussions to be had before going forward with this?

Copy link
Contributor

@evan-bradley evan-bradley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good to me, thanks @VihasMakwana.

@paulojmdias
Copy link
Member

Hi @VihasMakwana, great work, fantastic stuff! 💪

Do you think the points raised in this issue might also be worth evaluating for inclusion in this RFC (or perhaps in a follow-up)?

The use case is a bit different, but there seems to be real value for users in merging configurations dynamically from multiple files within a directory.

@VihasMakwana VihasMakwana changed the title [RFC] - Configuration Merging revamped [chore][RFC] - Configuration Merging revamped Sep 15, 2025
@VihasMakwana
Copy link
Contributor Author

@paulojmdias I like that enhancement! It can definitely be a follow-up in my opinion.

@paulojmdias
Copy link
Member

@paulojmdias I like that enhancement! It can definitely be a follow-up, in my opinion.

Awesome, let's see what other people think about it. I'm also open to help with it 🙌

@mx-psi
Copy link
Member

mx-psi commented Sep 15, 2025

This was discussed in the last Operator SIG meeting with no major concerns. I think we can merge this on or after Wednesday unless we hear any objections

@swiatekm
Copy link
Contributor

Speaking on behalf of the operator SIG, we're unlikely to use this feature, so no objections from our side.

@mx-psi mx-psi added this pull request to the merge queue Sep 17, 2025
Merged via the queue into open-telemetry:main with commit cb24802 Sep 17, 2025
57 of 58 checks passed
@github-actions github-actions bot added this to the next release milestone Sep 17, 2025
@douglascamata
Copy link
Member

Lovely so see this merged, @VihasMakwana. Congrats and thank you!

Do you have an implementation plan in mind? Are there are issues or PRs that I can follow to know when work starts to move forward so that I help out and/or know when it's ready to be used?

@VihasMakwana
Copy link
Contributor Author

@douglascamata Hello!

Do you have an implementation plan in mind? Are there are issues or PRs that I can follow to know when work starts to move forward so that I help out and/or know when it's ready to be used?

You can keep an eye on #13551. It's still in draft and I need to clean things up. I'll find some time this week/next one and mark it for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants