Skip to content

chore(inputs.elasticsearch_query): Migrate to official ES client#19288

Open
alespour wants to merge 9 commits into
influxdata:masterfrom
bonitoo-io:fix/es-query-plugin-client-v5-v6
Open

chore(inputs.elasticsearch_query): Migrate to official ES client#19288
alespour wants to merge 9 commits into
influxdata:masterfrom
bonitoo-io:fix/es-query-plugin-client-v5-v6

Conversation

@alespour

@alespour alespour commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Refactors plugin code to use official ES client instead of unmaintained olivere/elastic.
  • Adds ES 5 integration test coverage.

Node discovery caveats

As results of official clients limitations:

  • ES 5 configurations using enable_sniffer = true will now fail explicitly because the official v5 client does not support node discovery.
  • ES 6 client in-flight node discovery cannot be cancelled because DiscoverNodes() has no context support. Shutdown waits for it to finish, bounded by the configured HTTP timeout (5s by default).

Checklist

Related issues

resolves #19212

@telegraf-tiger telegraf-tiger Bot added fix pr to fix corresponding bug plugin/input 1. Request for new input plugins 2. Issues/PRs that are related to input plugins labels Jul 15, 2026
@alespour alespour changed the title fix(inputs.elasticsearch_query): migrate plugin code to official ES clients fix(inputs.elasticsearch_query): migrate plugin code to official clients Jul 16, 2026
@alespour
alespour marked this pull request as ready for review July 16, 2026 12:01
@srebhan srebhan changed the title fix(inputs.elasticsearch_query): migrate plugin code to official clients chore(inputs.elasticsearch_query): Migrate to official ES client Jul 16, 2026
@telegraf-tiger telegraf-tiger Bot added the chore label Jul 16, 2026

@srebhan srebhan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @alespour for your work! I have some comments...

Comment thread plugins/inputs/elasticsearch_query/client.go Outdated
Addresses: cfg.urls,
Username: cfg.username,
Password: cfg.password,
Transport: roundTripper{client: cfg.httpClient},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need this? Can't we simply do

Suggested change
Transport: roundTripper{client: cfg.httpClient},
Transport: cfg.httpClient.Transport,

@alespour alespour Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unlike Transport, http.Client enforces timeout during the whole request-response lifecycle including response body-reading.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we really need this?

@alespour alespour Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think so, without http.Client wrapper, stalled response during discovery could hang (discovery v6 is not cancellable via context). And that would block plugin shutdown via Stop().
But Client also handles cookies. Passing only Transport would lose it. We would have to handle cookies ourselves to cover cases when users enable auth cookie.

Comment thread plugins/inputs/elasticsearch_query/client.go Outdated
Comment thread plugins/inputs/elasticsearch_query/client.go Outdated
Comment thread plugins/inputs/elasticsearch_query/client.go Outdated
Comment thread plugins/inputs/elasticsearch_query/client.go Outdated
Comment thread plugins/inputs/elasticsearch_query/client.go Outdated
Comment thread plugins/inputs/elasticsearch_query/client.go Outdated
Comment thread plugins/inputs/elasticsearch_query/elasticsearch_query_test.go Outdated
Comment thread plugins/inputs/elasticsearch_query/elasticsearch_query_test.go Outdated
@srebhan srebhan self-assigned this Jul 16, 2026
@srebhan srebhan added area/elasticsearch and removed fix pr to fix corresponding bug labels Jul 16, 2026
@srebhan

srebhan commented Jul 17, 2026

Copy link
Copy Markdown
Member

@alespour regarding the question if we need the v5 client if code works with v6, I'd say no. If you can cover multiple ES server versions with one client, go for it as long as this doesn't limit the queries or anything...

@alespour

alespour commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

@srebhan Thank you very much for very thorough review. I'm overhauling the PR.

@telegraf-tiger

Copy link
Copy Markdown
Contributor

Download PR build artifacts for linux_amd64.tar.gz, darwin_arm64.tar.gz, and windows_amd64.zip.
Downloads for additional architectures and packages are available below.

⚠️ This pull request increases the Telegraf binary size by 1.94 % for linux amd64 (new size: 314.2 MB, nightly size 308.2 MB)

📦 Click here to get additional PR build artifacts

Artifact URLs

. DEB . RPM . TAR . GZ . ZIP
amd64.deb aarch64.rpm darwin_amd64.tar.gz windows_amd64.zip
arm64.deb armel.rpm darwin_arm64.tar.gz windows_arm64.zip
armel.deb armv6hl.rpm freebsd_amd64.tar.gz windows_i386.zip
armhf.deb i386.rpm freebsd_armv7.tar.gz
i386.deb ppc64le.rpm freebsd_i386.tar.gz
mips.deb riscv64.rpm linux_amd64.tar.gz
mipsel.deb s390x.rpm linux_arm64.tar.gz
ppc64el.deb x86_64.rpm linux_armel.tar.gz
riscv64.deb linux_armhf.tar.gz
s390x.deb linux_i386.tar.gz
linux_mips.tar.gz
linux_mipsel.tar.gz
linux_ppc64le.tar.gz
linux_riscv64.tar.gz
linux_s390x.tar.gz

@alespour

alespour commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@srebhan PR is overhauled. Plugin uses concrete client implementation for each major version (v5, v6). Version-independent logic is now in shared functions (code blocks moved client_v5.go -> client.go, without logic change).

One thing that requires maintainers decision: v5 client does not support discovery. Now, when enable_sniffer = true and v5 backend detected, plugin fails to start with an error. This is a breaking change.

I can think of 3 solutions:

  • a) document it as breaking change
  • b) log warning and silently ignore
  • c) use v6 client, but, there is a but - although all tests (unit and integration) using v6 client against ES v5 backend pass OK, there is no official guarantee about v6 client compatibility with older ES versions. Just empirical validation with tests. REST interface seems stable between v5 and v6 and official client is fairly low-level, so the fact that tests pass is fairly predictable.

@alespour
alespour requested a review from srebhan July 20, 2026 15:07
@srebhan

srebhan commented Jul 21, 2026

Copy link
Copy Markdown
Member

Thanks @alespour for the update, I will review in a minute...

Regarding

I can think of 3 solutions:

Please issue a warning and silently ignore discovery for v5. I think this is best as it is unlikely someone is still on 5.x and uses discovery. Maybe also add a short comment in the README.md that v5 does not support discovery.

@srebhan srebhan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice update @alespour! Some more comments from my side...

return "", 0, fmt.Errorf("parsing server version %q failed: %w", info.Version.Number, err)
}

return info.Version.Number, int(version.Major()), nil

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we please simply return the uint64 instead of casting this?

Comment on lines +99 to +101
if statusCode <= 299 {
return nil
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So we do accept 1xx codes as good?

Comment on lines +144 to +146
if interval <= 0 {
return
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we please check this outside of this function?!

Comment on lines +134 to +138
ctx, cancel := context.WithCancel(context.Background())

var wg sync.WaitGroup
wg.Add(1)
go func() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would prefer to do the goroutine setup on the caller side and pass the context in here. This saves the returned function and makes it clear that the client runs another thread...


// startDiscovery runs node discovery immediately and repeats it when the interval is positive.
// The returned function stops the loop and waits for any active call to return.
func startDiscovery(log telegraf.Logger, interval time.Duration, discover func(context.Context) error) func() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you please make log the last argument?! We usually do this in other places...

Comment on lines +23 to 26
if cfg.enableSniffer {
cfg.httpClient.CloseIdleConnections()
return nil, errors.New("enable_sniffer is not supported by the official ElasticSearch v5 client")
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we please check this on the caller side?

}
var result map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
return nil, err

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we please prefix the error to easier find the spot when we get a bug-report?

Suggested change
return nil, err
return nil, fmt.Errorf("decoding message body failed: %w", err)

}
}

func (e *ElasticsearchQuery) Start(telegraf.Accumulator) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we please merge this with newClient?!

QueryPeriod: config.Duration(time.Second * 600),
},
expected: "Error 404 (Not Found): no such index",
expected: "received error 404 (Not Found): no such index",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please revert. The function should use require.ErrorContains which can perfectly handle prefixed errors. This makes the test more robust if we change the prefix later.

Same below.

Comment on lines +900 to +901
require.ErrorContains(t, err, `parsing server version "invalid" failed`)
require.NotContains(t, err.Error(), "getting server version failed")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this really occur? I thought those code paths are mutually exclusive.

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

Labels

area/elasticsearch chore plugin/input 1. Request for new input plugins 2. Issues/PRs that are related to input plugins

Projects

None yet

Development

Successfully merging this pull request may close these issues.

inputs.elasticsearch_query: Migrate plugin code to official client

2 participants