Skip to content

Enhance tunnel-target-matches functionality and improve tests#886

Merged
karimra merged 3 commits into
mainfrom
vfixesv2
Jul 5, 2026
Merged

Enhance tunnel-target-matches functionality and improve tests#886
karimra merged 3 commits into
mainfrom
vfixesv2

Conversation

@karimra

@karimra karimra commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

karimra added 2 commits July 4, 2026 21:16
- Added a new test case for posting a configuration with a timeout duration string in `apiserver_extra_http_test.go`.
- Updated `ConfigApplyRequest` struct in `apply.go` to include `mapstructure` tags for better decoding.
- Improved the decoding process in `handleConfigTunnelTargetMatchesPost` to use a custom decoder with a duration hook.
- Refactored the `getTunnelTargetMatch` function in `tunnel_server.go` to simplify the matching logic.
- Enhanced the `ToStore` method in `config.go` to mirror tunnel server targets into the tunnel-target-matches store.
- Added a regression test in `file_output_test.go` to ensure closing stdout does not affect the process's standard output.
@karimra karimra merged commit bf3d9fc into main Jul 5, 2026
11 checks passed
@karimra karimra deleted the vfixesv2 branch July 5, 2026 19:05
drewelliott added a commit to drewelliott/gnmic that referenced this pull request Jul 6, 2026
Adds a second OTLP transport that complements the existing gRPC path, plus
a series of lifecycle and correctness fixes that apply to both transports.

OTLP/HTTP transport
-------------------
* New protocol value http (in addition to grpc). The endpoint accepts either
  a bare host:port (port required) or a full URL; bare endpoints are
  normalized to http://host:port/v1/metrics or https://host:port/v1/metrics
  based on whether tls is configured.
* Full URLs preserve scheme and path; empty or "/" path defaults to
  /v1/metrics. Only http and https schemes are accepted; userinfo in URLs
  is rejected (auth belongs in headers).
* mTLS support via the existing tls config block (ca-file, cert-file,
  key-file, skip-verify). http:// + a configured tls block is rejected at
  Init to prevent silent mTLS bypass.
* Configurable per-request body compression (compression: gzip|none) for
  HTTP; defaults to gzip. The compression field is ignored by the gRPC
  transport. Content-Type and Content-Encoding headers are protocol-owned
  and cannot be overridden by user-supplied headers.
* HTTP error classification per OTLP spec: 429/502/503/504 are retryable,
  all other 4xx/5xx are permanent. Transport-level errors (no HTTP status)
  are also retryable.
* Retry-After honored on 429/503 up to a 30s cap; otherwise exponential
  backoff with full jitter via math/rand/v2 (no global mutex).
* OTLP PartialSuccess (HTTP and gRPC) is treated as a permanent failure —
  per spec, retrying would duplicate the data points the server accepted.
  Accounting is request-level: a partially-successful batch counts toward
  sent events (full and partial success share one recordBatchSent path);
  the data-loss signal is gnmic_otlp_output_rejected_data_points_total.
* HTTP redirects are never followed (CheckRedirect returns
  ErrUseLastResponse); a redirect response classifies as a permanent
  error. Go forwards user-defined headers cross-host and 307/308 replay
  the POST body, so following redirects would let a redirecting (or
  compromised) collector pull the payload and headers to another origin.
* With compression: none, a user-supplied Content-Encoding header is
  stripped rather than passed through, so the uncompressed body is never
  mislabelled.
* A retry sleep interrupted by cancellation (worker restart or Close)
  counts the batch as failed rather than silently dropping it from the
  accounting.

Endpoint validation
-------------------
* Bare endpoints (both transports) must include an explicit port via
  net.SplitHostPort. Missing-port or trailing-colon forms are rejected
  at Init rather than failing on first dial against the wrong port.
* gRPC schemed forms (dns:///, unix:///, xds:///) pass through unchanged
  to grpc.NewClient.

Config validation
-----------------
* Numeric options are validated after defaulting: non-positive
  batch-size, interval, buffer-size, num-workers or timeout, and a
  negative max-retries are rejected at Init and on reload. Previously a
  negative value panicked at runtime (make(chan)/wg.Add at Init;
  make([])/time.NewTicker inside a worker goroutine, killing the whole
  process), silently disabled deadlines, or silently dropped every batch.

Live config reload
------------------
* outputState atomically bundles (cfg, *transportState) under
  atomic.Pointer. sendBatch loads once and uses the captured pointer for
  both conversion and dispatch, so a reload cannot tear the cfg/transport
  pair mid-batch.
* transportState holds the per-protocol client plus an inFlight WaitGroup
  shared across config-only reloads layered on top of the same transport.
  Update's rebuild path waits on the transport's inFlight before tearing
  down conn/Transport, eliminating wall-clock cleanup heuristics and the
  config-only-then-rebuild race where a fresh empty WaitGroup would have
  let the rebuild close a transport still in use.
* Update defers dynCfg.Store and state.Swap until after all fallible work
  (buildEventProcessors, buildOutputState) succeeds, so a transport-build
  failure no longer leaves processors partially applied.
* Header-only reloads take effect on the next batch without a transport
  rebuild — headers are read from state.cfg.Headers per request, not
  cached at Init.
* Init reorder: register metrics, then build resource state, then start
  workers. A failure after metrics register triggers a deferred
  unregister so a subsequent Init does not collide with the registry.

Per-instance metric ownership
-----------------------------
* otlpOutput tracks the prometheus collectors it actually registered.
  A failed Init or Close that calls unregisterMetrics() removes only this
  instance's registrations, not collectors another OTLP output owns in a
  shared registry.

Worker lifecycle
----------------
* worker takes its *sync.WaitGroup as a parameter rather than reading
  o.wg at goroutine start, eliminating a race against Update's
  o.wg = newWG when a not-yet-scheduled worker would otherwise call
  Done() on the new WaitGroup and deadlock oldWG.Wait().
* Close waits on transport.inFlight before cleanup, making it symmetric
  with Update's rebuild path and robust against future non-worker
  sendBatch callers.
* Close no longer closes the event channel: the outputs manager
  dispatches Write/WriteEvent goroutines without synchronizing against
  Close, so a concurrent producer could panic the process with a send on
  a closed channel. Workers exit via context cancellation instead, and
  Write/WriteEvent drop events once state is torn down rather than
  dereferencing nil.

Headers
-------
* The headers config field attaches key/value pairs to every export
  request, sent as gRPC metadata when protocol is grpc and as HTTP
  headers when protocol is http. Useful for multi-tenant backends
  (Grafana Mimir/Loki/Tempo) that route via X-Scope-OrgID.
* String() redacts header values (key names stay visible), so
  config-reload logging cannot leak credentials such as Authorization
  bearer tokens placed in headers.

Documentation
-------------
* Rewrites the OTLP output user guide with an example-first layout
  (gRPC, HTTP, HTTP+mTLS examples up front), a full configuration
  reference table, and dedicated sections for transport behavior,
  TLS/compression, metric naming, type detection, resource attributes,
  headers, and retries.
* Documents the redirect policy, header-value redaction, the
  partial-success accounting definition (alert on the rejected
  data-points counter to detect data loss), and the buffer-size reload
  caveat.

Tests
-----
* mTLS test server harness shared by HTTP tests.
* TLS coherence, scheme rejection, userinfo rejection, port-required,
  Retry-After parsing, PartialSuccess handling (HTTP and gRPC), gzip
  body compression with content-encoding header, race regression for
  worker-restart, two-step reload race regression for transportState
  sharing, real Update() reload-path coverage (channel swap, worker
  restart, header-only reload, endpoint change), Init-failure metrics
  unregistration, and end-to-end OTLP/HTTP delivery against a real
  mTLS server.
* Redirect refusal (origin hit exactly once, redirect target never
  contacted, error permanent), case-insensitive Content-Encoding strip
  with compression: none, partial-success accounting (an all-rejected
  batch still counts as sent and is not retried), header-value redaction
  with copy semantics, numeric config validation table, and a concurrent
  WriteEvent-vs-Close race regression; the cancelled-retry test also
  asserts the dropped batch is counted as failed.
* Rebase note: renames one of the two identically-named
  TestOTLP_InitCompilesCounterPatterns functions that main currently
  carries from the openconfig#886/openconfig#887 merge (the duplicate prevents the package's
  tests from compiling) and adapts the counter-patterns tests to the
  outputState accessor introduced here.
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.

2 participants