Commit 1f0884a
* fix(opensearchtransport): honor per-request header override over global headers
setReqGlobalHeader compared the existing header *value* against the
header *name* (req.Header.Get(k) != k), which is effectively always
true, so global headers were appended even when the caller had already
set the same key on the request. Compare for presence instead so a
per-request header suppresses the matching global default.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* fix(opensearchtransport): prevent gzip buffer-pool nil poisoning on compress error
When io.Copy or writer.Close failed, compress() returned (nil, err) but
Perform had already armed `defer collectBuffer(buf)` with that nil,
which Put a typed-nil *bytes.Buffer into the sync.Pool. The next
compress() call would Get() the nil and panic on buf.Reset().
Return the buffer on the error path so the deferred collectBuffer
recycles it, and nil-guard collectBuffer for belt-and-suspenders.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* fix(opensearchtransport): surface body-read errors during response buffering
The post-RoundTrip buffering block in Perform declared a fresh `err`
via :=, shadowing the outer return error. On a truncated body or
context cancellation mid-read, the read error was silently dropped
and res.Body was left as the (now closed) original, so callers
received a 2xx Response with an unreadable body and err == nil.
Always replace res.Body with the bytes that were read (so callers see
a partial body rather than a closed reader), and propagate the read
error when no earlier error is pending.
Adjust Client.Do to handle the new (resp != nil, err != nil) case from
Perform: branch on resp == nil for the hard-failure path and wrap the
error in ErrReadBody when a response is available, preserving the
documented contract that TestClientInterfe asserts.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* fix: close error-response body in ParseError and v5preview do()
With Config.DisableResponseBuffering=true, Perform returns the live
http.Response.Body. ParseError() drained it via io.ReadAll but never
called Close(), so the connection was not returned to http.Transport's
idle pool until GC ran the finalizer. Under sustained 4xx/5xx load this
exhausts FDs.
Close the body in ParseError after reading (a no-op NopCloser in the
buffered case), and drain+close in the v5preview do() branch where
dataPointer == nil and ParseError is bypassed entirely. The legacy
opensearchapi and plugins/{ism,security} do() helpers already route the
non-nil-body case through ParseError, so they're covered by the same
change.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* fix(transport): classify Perform body-read errors via sentinel, keep v5preview error body readable
Address review feedback on #859:
- Add opensearchtransport.ErrResponseBodyRead sentinel; Perform wraps
response-buffering read failures with it. Client.Do now classifies via
errors.Is so an unrelated transport error returned alongside a response
(e.g. context cancellation during retry backoff after a retryable status)
is no longer mislabeled ErrReadBody. errors.Is(err, context.Canceled)
still holds, but the misleading "failed to read body" prefix is gone.
- v5preview do() no-decode error path reads the body to EOF then re-wraps
it in a NopCloser instead of discarding it, keeping resp.Body readable and
consistent with the ParseError path while still freeing the connection
under DisableResponseBuffering.
- Document the (resp != nil, err != nil) invariant on the Interface contract
and Perform godoc: callers must treat resp == nil, not err != nil, as the
signal for a hard transport failure.
- Scope the CHANGELOG body-close claim to non-buffered mode (in the default
buffered mode Perform already drains and closes the body).
- Add unit tests for the gzip nil-poisoning fix, header-override suppression
(asserting len == 1, which the prior value-only test could not detect),
body-read error surfacing, the Do classification split, and the v5preview
body-readability regression.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* chore(osgen): silence gochecknoglobals on decodeEquivalentGroups lookup table
Pre-existing lint warning introduced in f95f305 (#844); the table is
a static codegen lookup intentionally kept package-level next to its
doc comment and the funcs that consult it.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* fix(transport,signer): drain bodies on raw RoundTrip paths and close request body on signer read error
Follow-up to the review on #859, addressing the pre-existing instances of the
same "close/drain the body" bug class that Jakob's audit surfaced. These are on
direct RoundTrip paths that lack Perform's response-buffering safety net, so
closing a partially-read body genuinely defeats HTTP keep-alive.
- cluster_health.go stats poller and discovery.go's /_cat/shards,
/_cluster/state/metadata, and /_nodes paths now drain to EOF before close via
a deferred drain-then-close. This covers both the non-200 early returns and
the json.Decode success paths, which stop at the end of the JSON value without
consuming trailing bytes. The drain/close is inlined at each defer (rather
than extracted to a helper) so bodyclose can verify the close statically.
- opensearch.Response.String() is now non-consuming: it restores Body with an
in-memory reader after rendering, so logging a response no longer empties a
body other code expects to read. Receiver changed to a pointer so the restore
is visible to callers (Response is always used as *Response).
- The AWS v1 and v2 signers now close the request body on the read-error path
in hexEncodedSha256OfRequest.
- Add tests: Response.String non-consuming, and both signers closing the
request body when the read fails.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* refactor(transport): split Perform into Stream + buffering wrapper; drop DisableResponseBuffering
Add (*Client).Stream(req) holding the full request/retry/RoundTrip loop
with seed fallback, returning the raw RoundTrip body. The caller owns
reading and closing res.Body; the (res != nil, err != nil) invariant and
the req.URL.Host rewrite side effect are preserved.
(*Client).Perform becomes a thin buffering wrapper over Stream: io.ReadAll
+ bytes.Reader + NopCloser, with body-read failures surfaced via
ErrResponseBodyRead. Marked Deprecated for removal in v5.
Add (*opensearch.Client).Stream as a passthrough so raw consumers do not
need to type-assert c.Transport. The transport's Stream is exposed via
the new opensearch.Streamer interface; callers reach it through
ErrTransportMissingMethodStream when the configured transport does not
satisfy it. Mark (*opensearch.Client).Perform as deprecated.
Remove the untagged DisableResponseBuffering field from opensearch.Config
and opensearchtransport.Config, the disableResponseBuffering struct field,
and the constructor assignment. Perform now always buffers; Stream never
does. Add a TODO on opensearchtransport.Interface noting v5 should add
Stream and remove Perform.
Replace the two DisableResponseBuffering test sites with table-driven
TestPerformStreamBuffering and TestStreamNilBody covering both entry
points, body lifecycle, and the req.URL.Host rewrite. Preserve
TestPerformSurfacesBodyReadError and TestDoPerformErrorClassification.
Migrate opensearchutil/bulk_indexer_integration_test.go's raw Perform
polling loop to the typed client.Cluster.Health call so the new Perform
deprecation does not trigger staticcheck SA1019.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* build(lint): run golangci-lint across all build-tag combinations
golangci-lint compiles one point in the build-tag space per run, so a single
invocation can never lint every file. Two mutually-exclusive boolean axes
partition the tree:
- integration vs !integration: every unit *_test.go is `!integration`; every
integration test is `integration`. Setting `integration` drops all unit
test files from the type-check; omitting it drops all integration ones.
- multinode vs !multinode (within integration): the single-node integration
files are `!multinode`; the multinode ones require `multinode`.
make lint.local and the CI workflow previously passed a single tag set that
included `integration` and `multinode`, so they silently skipped every unit
test file AND every single-node integration file -- those were never linted.
Feature tags (core, plugins, plugin_security, plugin_index_management) are
pure-OR alternatives that union harmlessly into every run, so complete coverage
needs one run per (integration, multinode) combination. Introduce
GOLANGCI_LINT_TAG_SETS enumerating the three runs, loop over it in lint.local
and the Docker `linters` target, and add a matrix to the CI workflow.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* test(lint): resolve issues surfaced by linting all build-tag combinations
Running golangci-lint across every (integration, multinode) build-tag
combination (see the preceding build(lint) commit) compiles source files
that the previous single-tag-set invocation never type-checked -- every
!integration unit test and every !multinode single-node integration test.
That exposed pre-existing lint findings in files that had never been linted.
This commit clears them; it is a test/quality cleanup with no runtime
behavior change.
- testifylint: replace assert.* error assertions with require.* (require-error)
across opensearchapi, opensearchtransport, opensearchutil, plugins, signer,
and root tests; switch require.IsType on error values to ErrorAs; convert
exact float comparisons to require.InDelta (float-compare).
- staticcheck: pass t.Context() instead of a nil context in
opensearch_integration_test.go (SA1012); drop a dead append (SA4010).
- gci: fix the import grouping in opensearch_integration_test.go.
- thelper: add t.Helper() to table-driven check closures in error_test.go
and api_indices_response_test.go.
- tparallel: call t.Parallel() in the TestConnectionPoolPromotion subtests.
- gosec: validate PPROF_ADDR and stop echoing the env value into a log call
in connection_benchmark_test.go (G706).
- gocritic: avoid append-to-different-slice aliasing in discovery_internal_test.go.
- unparam: drop the always-nil roles arg path and unused name param in test
helpers, and assert WriteTo's byte count so its result is used; add a
RolePolicy test that exercises a dead, role-bearing connection.
- goconst: replace repeated action/policy-name string literals with constants
in opensearchutil/bulk_indexer.go and opensearchtransport policy files.
- lll: wrap over-long JSON fixtures and string literals.
No behavior change; test-only.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* refactor(osgen): emit net/http method constants in generated Req tests
The Req-test fragment template hard-coded the HTTP method as a quoted
string literal (e.g. "POST"), so every generated *_gen_test.go pinned
the wantMethod field to a magic string. The dispatch and plugin
fragments already route methods through HTTPMethodConst to produce
http.MethodPost et al.; the test fragment was the lone holdout.
Wire the existing HTTPMethodConst helper into the reqTest template via
a methodExpr func and add net/http to the fragment's import set so the
emitted test files reference http.MethodPost instead of "POST". Unknown
methods still fall through to a quoted literal, preserving today's
behavior for non-standard verbs.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* chore(v5preview): regenerate Req tests to use net/http method constants
Regenerated output of the osgen test fragment change: every wantMethod
literal in v5preview/opensearchapi/*_gen_test.go switches from a quoted
string ("GET", "POST", ...) to the corresponding net/http constant
(http.MethodGet, http.MethodPost, ...), and net/http is added to each
file's import set.
No behavioral change — the emitted constants resolve to the same
strings at runtime; this commit is purely the result of re-running
osgen after the template update.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* remove unused file
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* fix(osgen): make errwrap catalogs functions to drop mutable globals
errwrap.Wrappers and errwrap.OperationWrappers were exported mutable package
vars carrying //nolint:gochecknoglobals directives. Convert both to functions
that return a fresh slice/map. This removes the gochecknoglobals findings
honestly (no suppression), and makes the read-only catalogs immutable from a
caller's perspective -- callers can no longer mutate the shared backing
slice/map. The only consumers are in-package (For, sortedCanonical); update
them to call the functions.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* chore(osgen): drop dead body-drain in plugin do() no-decode path
opensearch.Do routes through the buffered (*opensearchtransport.Client).Perform,
so resp.Body in the plugin do[T] helpers is already an io.NopCloser over
a bytes.Reader -- the connection has been drained and returned to the
pool. The no-decode error-path drain added in PR #859 was only meaningful
when DisableResponseBuffering=true, which has been removed.
Strip the drain from the cmd/osgen plugin client template and the four
hand-written copies (opensearchapi, v5preview/opensearchapi,
plugins/security, plugins/ism). The helper now reads:
if resp.IsError() {
if dataPointer != nil {
return resp, opensearch.ParseError(resp)
}
return resp, fmt.Errorf("status: %s", resp.Status())
}
with a doc comment noting resp.Body has already been buffered and closed
by Perform.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* chore(v5preview): regenerate plugin client_gen.go
Output of `make regen` after the cmd/osgen template change in the
preceding commit. No hand edits.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* docs(buffering): rewrite guide for Do[T] vs Stream
Rewrite guides/response_buffering.md around the two entry points:
opensearch.Do[T] (typed, buffered, default; SDK owns the body) versus
opensearchtransport.Client.Stream (raw, unbuffered, caller owns the body).
Document the reason there is intentionally no typed streaming helper, and
update the proxy example to use client.Stream(req).
CHANGELOG: replace the DisableResponseBuffering "Added" entry with the
Stream/Client.Stream "Added" entry; add a Deprecated entry for Perform on
both opensearchtransport.Client and opensearch.Client (removal in v5);
drop the stale DisableResponseBuffering reference from the #859 Fixed
entry.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* test(benchmarks): fix goroutine leak and stateful body in transport benches
Three correctness/quality fixes on the transport-adjacent benchmarks
surfaced while running locally with PGO collection. No production code
changes.
opensearch_benchmark_test.go (BenchmarkClient/Create client with defaults):
Each iteration constructed a fresh opensearch.NewClient, which spawns
the transport's per-client cluster-health and node-stats goroutines.
Nothing closed them, so goroutines leaked linearly with b.N and at
high iteration counts the Go runtime starved the bench loop itself.
Close the underlying *opensearchtransport.Client in each iteration so
the background goroutines exit.
opensearchtransport/opensearchtransport_benchmark_test.go (BenchmarkTransport):
- The pre-existing FakeTransport stored a single *http.Response with a
strings.Reader body and returned the same pointer from every
RoundTrip. strings.Reader is stateful: after the first Perform
drained it the next iteration saw EOF, so the bench was measuring
the EOF-handling path rather than steady-state Perform. Build a
fresh response (with a fresh body) per RoundTrip.
- Hoist opensearchtransport.New out of the per-iteration loop. Real
callers build one transport per process; constructing one per
iteration both inflates the measurement and (on this branch) leaks
health-check goroutines. Disable the load-shedding poller via
NodeStatsInterval = -1 so its tick rate doesn't bleed into the
measurement.
opensearchtransport/logger_benchmark_test.go (BenchmarkTransportLogger):
Same construction-per-iteration anti-pattern across all four
Text/Text-Body/JSON/JSON-Body sub-benches. Collapse the four
copy-pasted bodies into a single closure, hoist New out of the loop,
add b.Cleanup to close the transport, and disable the load-shedding
poller. The Text-Body case had a separate bug: it called
res.Body.Close() before io.ReadAll(res.Body), so the read always
returned 0 bytes against a closed body and the len < 13 branch was
silently flagged. Read first, then close.
Drive-by: switch error format verbs from %s to %q in the touched
Fatalf/Errorf sites for safer rendering of error chains.
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
* feat(github/workflows): pin remaining actions to SHA (#882)
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
---------
Signed-off-by: Sean Chittenden <sean.chittenden@crowdstrike.com>
Co-authored-by: Jakob <jakob.hahn@hetzner.com>
1 parent 4fd2edf commit 1f0884a
614 files changed
Lines changed: 2860 additions & 1615 deletions
File tree
- .github/workflows
- cmd/osgen
- emit
- errwrap
- guides
- internal/build
- opensearchapi
- testutil
- opensearchtransport
- opensearchutil
- plugins
- ism
- security
- signer
- awsv2
- aws
- v5preview/opensearchapi
- plugins
- asynchronous_search
- flow_framework
- geospatial
- ingestion
- insights
- ism
- knn
- list
- ltr
- ml
- neural
- notifications
- observability
- ppl
- query
- replication
- rollups
- search_relevance
- security_analytics
- security
- sm
- sql
- transforms
- ubi
- wlm
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
12 | | - | |
| 11 | + | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
12 | 24 | | |
13 | 25 | | |
14 | 26 | | |
| |||
17 | 29 | | |
18 | 30 | | |
19 | 31 | | |
20 | | - | |
| 32 | + | |
21 | 33 | | |
22 | 34 | | |
23 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
54 | | - | |
| 54 | + | |
55 | 55 | | |
56 | | - | |
| 56 | + | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| |||
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
| 205 | + | |
205 | 206 | | |
206 | 207 | | |
207 | 208 | | |
| |||
210 | 211 | | |
211 | 212 | | |
212 | 213 | | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
213 | 219 | | |
214 | 220 | | |
215 | 221 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
7 | 29 | | |
8 | 30 | | |
9 | 31 | | |
| |||
204 | 226 | | |
205 | 227 | | |
206 | 228 | | |
207 | | - | |
208 | | - | |
209 | | - | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
210 | 235 | | |
211 | 236 | | |
212 | 237 | | |
| |||
781 | 806 | | |
782 | 807 | | |
783 | 808 | | |
784 | | - | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
785 | 813 | | |
786 | 814 | | |
787 | 815 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
343 | 343 | | |
344 | 344 | | |
345 | 345 | | |
346 | | - | |
| 346 | + | |
347 | 347 | | |
348 | 348 | | |
349 | 349 | | |
350 | 350 | | |
351 | 351 | | |
352 | 352 | | |
353 | | - | |
| 353 | + | |
354 | 354 | | |
355 | | - | |
| 355 | + | |
356 | 356 | | |
357 | 357 | | |
358 | 358 | | |
359 | 359 | | |
360 | 360 | | |
361 | | - | |
| 361 | + | |
362 | 362 | | |
363 | 363 | | |
364 | 364 | | |
| |||
378 | 378 | | |
379 | 379 | | |
380 | 380 | | |
381 | | - | |
| 381 | + | |
382 | 382 | | |
383 | 383 | | |
384 | 384 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
149 | 154 | | |
150 | 155 | | |
151 | 156 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| 112 | + | |
112 | 113 | | |
113 | 114 | | |
114 | 115 | | |
| |||
134 | 135 | | |
135 | 136 | | |
136 | 137 | | |
137 | | - | |
| 138 | + | |
| 139 | + | |
138 | 140 | | |
139 | 141 | | |
140 | 142 | | |
| |||
148 | 150 | | |
149 | 151 | | |
150 | 152 | | |
151 | | - | |
| 153 | + | |
152 | 154 | | |
153 | 155 | | |
154 | 156 | | |
| |||
0 commit comments