Skip to content

feat: add dynamic target support - #1336

Open
ylighgh wants to merge 3 commits into
percona:mainfrom
ylighgh:feat/dynamic-targets
Open

feat: add dynamic target support#1336
ylighgh wants to merge 3 commits into
percona:mainfrom
ylighgh:feat/dynamic-targets

Conversation

@ylighgh

@ylighgh ylighgh commented Aug 5, 2026

Copy link
Copy Markdown

Summary

  • add a /probe endpoint for MongoDB targets supplied at scrape time
  • load credentials and TLS options from named YAML auth modules
  • reject credentials, paths, and query parameters in dynamic targets
  • redact credentials from connection URI logs
  • document dynamic target configuration and the new --config.file flag

Testing

  • make format
  • go test -tags gssapi -count=1 . -run '^Test(LoadAuthConfig|LoadAuthConfigRejectsInvalidModule|BuildDynamicURI|ResolveAuthModule|ValidateTargetConfiguration|DynamicTargetFactoryRejectsUnknownModule|RedactMongoURI)$'
  • go test -tags gssapi -count=1 ./exporter -run '^Test(DynamicTarget|DynamicTargetValidation|DynamicOnlyServer|ServerRequiresStaticOrDynamicTarget)$'
  • go build .
  • go run .github/check-license.go

The full integration suite requires the repository's MongoDB Docker sandbox; GitHub Actions runs that matrix.

Reviewer notes

The dynamic exporter handlers are cached by auth module and target so repeated Prometheus scrapes reuse MongoDB clients.

@ylighgh
ylighgh requested a review from a team as a code owner August 5, 2026 06:56
@ylighgh
ylighgh requested review from ademidoff and maxkondr and removed request for a team August 5, 2026 06:56
@it-percona-cla

it-percona-cla commented Aug 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@ylighgh
ylighgh force-pushed the feat/dynamic-targets branch from fcb9899 to 50da951 Compare August 5, 2026 06:59
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 49.04459% with 80 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.36%. Comparing base (dc46ed5) to head (e93381e).
⚠️ Report is 262 commits behind head on main.

Files with missing lines Patch % Lines
exporter/server.go 46.77% 26 Missing and 7 partials ⚠️
main.go 37.77% 27 Missing and 1 partial ⚠️
auth_config.go 62.00% 11 Missing and 8 partials ⚠️

❗ There is a different number of reports uploaded between BASE (dc46ed5) and HEAD (e93381e). Click for more details.

HEAD has 2 uploads less than BASE
Flag BASE (dc46ed5) HEAD (e93381e)
agent 10 8
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1336      +/-   ##
==========================================
- Coverage   70.88%   65.36%   -5.53%     
==========================================
  Files          28       30       +2     
  Lines        3569     2616     -953     
==========================================
- Hits         2530     1710     -820     
+ Misses        904      746     -158     
- Partials      135      160      +25     
Flag Coverage Δ
agent 65.36% <49.04%> (-5.53%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 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.

@ylighgh

ylighgh commented Aug 21, 2026

Copy link
Copy Markdown
Author

Hi @ademidoff @maxkondr, the PR has been updated with the latest main. Could you please take another look when you have time? Thanks!

@ademidoff

ademidoff commented Aug 25, 2026

Copy link
Copy Markdown
Member

Thanks for the contribution, and for splitting the target string validation out so carefully — rejecting credentials, paths and query params in parseDynamicTarget is the right call, and the /probe shape matches what people already expect from blackbox_exporter.

A few notes from reading through it.

1. The note on connection reuse overstates what the cache does

The dynamic exporter handlers are cached by auth module and target so repeated Prometheus scrapes reuse MongoDB clients.

The cache in newDynamicTargetFactory (main.go) stores the *Exporter/http.Handler, not the connection. The cached Exporter still goes through getClient, which honours GlobalConnPool — and dynamicOpts copies that flag through from the CLI unchanged. Since --mongodb.global-conn-pool defaults to false (main.go:47 has no default: tag), the out-of-the-box behaviour on /probe is still connect-and-disconnect per scrape, exactly as on /metrics.

What the cache genuinely buys is that it makes the pooled path reachable for dynamic targets at all: e.client is per-Exporter state, so without caching the Exporter a fresh one per scrape would make --mongodb.global-conn-pool silently ineffective on /probe. That is a real and necessary property — worth stating that way in the PR description rather than as unconditional client reuse.

I have filed #1346 for the underlying reconnect-per-scrape default; that is pre-existing and explicitly not something this PR needs to solve.

2. The handler cache is unbounded

// ponytail: service-discovery targets are stable; add bounded eviction only if target churn becomes measurable.
handlers := make(map[string]http.Handler)

The reasoning holds for memory when pooling is off. But combined with --mongodb.global-conn-pool=true the failure mode is worse than growth in a map: every cached Exporter holds a live MongoDB client, and nothing ever evicts one. Target churn — a rescaled replica set, hosts rotating through service discovery, or simply an operator probing ad-hoc targets — then leaks open connections to MongoDB with no upper bound and no way to reclaim them short of a restart.

Given /probe accepts an arbitrary target from whoever can reach the endpoint, I would rather not merge an unbounded cache keyed on that input. A small bounded LRU with Disconnect on eviction, or a TTL sweep, would close it. Happy to discuss the shape if you would prefer a different approach.

3. Stray comment marker

The // ponytail: prefix above looks like a leftover tooling/agent artifact rather than an intentional convention — please drop it (the sentence itself is worth keeping, adjusted per the point above).

4. Heads-up on a likely conflict

redactMongoURI here fixes main.go:143, which is a genuine leak — thank you for catching it. That same line is covered by #1345, which I opened for the credential-logging problem repo-wide: there are five further call sites (exporter/seedlist.go:31, :36, :41, main.go:259, exporter/server.go:181) that log URIs at the default log level, so they are rather more exposed than the Debug-level one. A fix for those is in progress and puts the helper somewhere both package main and package exporter can import it, since your redactMongoURI is unexported in package main and exporter cannot reuse it.

Whichever of the two lands second will need a small rebase around main.go:143. Nothing for you to do now — just so it is not a surprise.

@ylighgh

ylighgh commented Aug 25, 2026

Copy link
Copy Markdown
Author

Thanks for the detailed review. Understood.

I’ll wait for the fix for #1345 and rebase after it lands. I’ll then update the PR description, replace the unbounded handler cache with a bounded cache that disconnects evicted exporters, and remove the ponytail comment.

Thanks for pointing these out.

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.

4 participants