Skip to content

fix(otel): normalize endpoint URL and infer insecure mode from scheme#362

Merged
gandalf-at-lerian merged 1 commit intodevelopfrom
fix/otel-endpoint-url-normalization
Mar 19, 2026
Merged

fix(otel): normalize endpoint URL and infer insecure mode from scheme#362
gandalf-at-lerian merged 1 commit intodevelopfrom
fix/otel-endpoint-url-normalization

Conversation

@gandalf-at-lerian
Copy link

Problem

otlptracegrpc.WithEndpoint() (and metric/log equivalents) expect host:port, not a full URL. Consumers commonly pass OTEL_EXPORTER_OTLP_ENDPOINT as http://host:4317.

When InsecureExporter is false (the default since v4), the gRPC SDK assumes TLS and appends :443, resulting in http://host:4317:443 — which fails with:

rpc error: code = Unavailable desc = invalid target address http://host:4317,
error info: address http://host:4317:443: too many colons in address

This affects all services on lib-commons v4 that don't explicitly set InsecureExporter: true (tenant-manager, matcher, etc).

Root Cause

Introduced in 387a994 (March 7, 2026) when InsecureExporter became a conditional flag. Previously WithInsecure() was always hardcoded in v2/v3.

Fix

Auto-detect scheme in CollectorExporterEndpoint during NewTelemetry():

  • http:// → strip scheme, set InsecureExporter = true
  • https:// → strip scheme, keep InsecureExporter as-is
  • No scheme → assume insecure (common in k8s internal comms)

Backwards-compatible: no consumer code changes needed. Existing consumers that pass http://host:4317 will work correctly without adding InsecureExporter: true.

Tests

5 table-driven test cases covering all normalization scenarios. All existing tests pass.

The gRPC WithEndpoint() expects host:port, not a full URL. When consumers
pass OTEL_EXPORTER_OTLP_ENDPOINT as 'http://host:4317' (standard format),
the SDK incorrectly appends :443, producing 'http://host:4317:443'.

This was introduced in 387a994 (March 7) when InsecureExporter became a
conditional flag. Previously WithInsecure() was always hardcoded. Consumers
that migrated to v4 without setting InsecureExporter=true got broken OTEL
export silently.

Fix: auto-detect scheme in CollectorExporterEndpoint:
- http:// → strip scheme, set InsecureExporter=true
- https:// → strip scheme, keep InsecureExporter as-is
- no scheme → assume insecure (common in k8s internal comms)

This is backwards-compatible: no consumer code changes needed.
@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Walkthrough

The pull request introduces endpoint normalization logic in the NewTelemetry function within the OpenTelemetry package. When the CollectorExporterEndpoint contains a URL scheme, the function strips the scheme and conditionally sets the InsecureExporter flag: http:// schemes result in an unsecured endpoint with the flag set to true, while https:// schemes preserve the secured mode with the flag set to false. Endpoints without schemes default to insecure mode. A new unit test validates these normalization rules across various endpoint and flag configurations.

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is comprehensive, explaining the problem, root cause, and fix; however, the required PR Checklist template sections are not completed. Complete the PR Checklist template by selecting PR type (Fix), checking all applicable checklist items, and confirming code is ready for review.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: endpoint URL normalization and insecure mode inference from the URL scheme.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@commons/opentelemetry/otel_test.go`:
- Around line 102-140: Add two test cases to the existing tests table in
otel_test.go: (1) a "schemeless with explicit secure intent preserved" case
using endpoint "otel-collector:4317" with insecureOverride set to true/false as
appropriate to represent an explicitly secure configuration (set
insecureOverride: false) and expect wantEndpoint "otel-collector:4317" and
wantInsecure false so schemeless secure intent is preserved; (2) a "schemeless
with surrounding whitespace trimmed" case using endpoint "  otel-collector:4317 
" (with surrounding spaces) and expect wantEndpoint "otel-collector:4317" and
wantInsecure true to verify whitespace is trimmed; add both rows to the tests
slice that contains fields name, endpoint, wantEndpoint, wantInsecure,
insecureOverride so the table-driven test runs these regressions.

In `@commons/opentelemetry/otel.go`:
- Around line 104-114: The endpoint normalization currently trims into a local
ep but fails to persist the trimmed value for the schemeless branch and
unconditionally sets cfg.InsecureExporter=true for schemeless endpoints, risking
a silent TLS downgrade; update the switch in the endpoint handling (the block
that inspects ep and sets cfg.CollectorExporterEndpoint and
cfg.InsecureExporter) so that you always write the trimmed ep back into
cfg.CollectorExporterEndpoint, set cfg.InsecureExporter=true only when the
scheme is explicitly "http://" and ensure "https://" sets or leaves
InsecureExporter=false; for the default (no scheme) branch do not change
cfg.InsecureExporter (just persist the trimmed endpoint) so TLS behavior isn’t
downgraded implicitly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 131a8b4d-befa-4677-b756-b16df0902e3e

📥 Commits

Reviewing files that changed from the base of the PR and between f0c5a62 and 0c038f2.

📒 Files selected for processing (2)
  • commons/opentelemetry/otel.go
  • commons/opentelemetry/otel_test.go

Comment on lines +102 to +140
tests := []struct {
name string
endpoint string
wantEndpoint string
wantInsecure bool
insecureOverride bool // initial InsecureExporter value
}{
{
name: "http scheme stripped and insecure inferred",
endpoint: "http://otel-collector:4317",
wantEndpoint: "otel-collector:4317",
wantInsecure: true,
},
{
name: "https scheme stripped and insecure stays false",
endpoint: "https://otel-collector:4317",
wantEndpoint: "otel-collector:4317",
wantInsecure: false,
},
{
name: "no scheme defaults to insecure",
endpoint: "otel-collector:4317",
wantEndpoint: "otel-collector:4317",
wantInsecure: true,
},
{
name: "https with explicit insecure override preserved",
endpoint: "https://otel-collector:4317",
insecureOverride: true,
wantEndpoint: "otel-collector:4317",
wantInsecure: true,
},
{
name: "http with trailing slash",
endpoint: "http://otel-collector:4317/",
wantEndpoint: "otel-collector:4317/",
wantInsecure: true,
},
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add regression cases for schemeless secure intent and whitespace normalization.

The table misses two high-impact scenarios in this change path: preserving secure intent for schemeless endpoints and trimming schemeless endpoints with surrounding whitespace.

Proposed test additions
 	{
 		name:         "no scheme defaults to insecure",
 		endpoint:     "otel-collector:4317",
 		wantEndpoint: "otel-collector:4317",
 		wantInsecure: true,
 	},
+	{
+		name:             "no scheme with explicit secure intent is preserved",
+		endpoint:         "otel-collector:4317",
+		insecureOverride: false,
+		wantEndpoint:     "otel-collector:4317",
+		wantInsecure:     false,
+	},
+	{
+		name:             "no scheme trims surrounding whitespace",
+		endpoint:         "   otel-collector:4317   ",
+		insecureOverride: true,
+		wantEndpoint:     "otel-collector:4317",
+		wantInsecure:     true,
+	},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commons/opentelemetry/otel_test.go` around lines 102 - 140, Add two test
cases to the existing tests table in otel_test.go: (1) a "schemeless with
explicit secure intent preserved" case using endpoint "otel-collector:4317" with
insecureOverride set to true/false as appropriate to represent an explicitly
secure configuration (set insecureOverride: false) and expect wantEndpoint
"otel-collector:4317" and wantInsecure false so schemeless secure intent is
preserved; (2) a "schemeless with surrounding whitespace trimmed" case using
endpoint "  otel-collector:4317  " (with surrounding spaces) and expect
wantEndpoint "otel-collector:4317" and wantInsecure true to verify whitespace is
trimmed; add both rows to the tests slice that contains fields name, endpoint,
wantEndpoint, wantInsecure, insecureOverride so the table-driven test runs these
regressions.

Comment on lines +104 to +114
if ep := strings.TrimSpace(cfg.CollectorExporterEndpoint); ep != "" {
switch {
case strings.HasPrefix(ep, "http://"):
cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "http://")
cfg.InsecureExporter = true
case strings.HasPrefix(ep, "https://"):
cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "https://")
default:
// No scheme — assume insecure (common in k8s internal comms).
cfg.InsecureExporter = true
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Prevent silent TLS downgrade and persist normalized endpoint.

Line [112]-[113] forces InsecureExporter = true for all schemeless endpoints, which can unintentionally downgrade secure setups. Also, Line [104] trims into ep, but the no-scheme branch never writes the trimmed value back to cfg.CollectorExporterEndpoint.

Proposed fix
 if ep := strings.TrimSpace(cfg.CollectorExporterEndpoint); ep != "" {
+    cfg.CollectorExporterEndpoint = ep
 	switch {
 	case strings.HasPrefix(ep, "http://"):
 		cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "http://")
 		cfg.InsecureExporter = true
 	case strings.HasPrefix(ep, "https://"):
 		cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "https://")
 	default:
-		// No scheme — assume insecure (common in k8s internal comms).
-		cfg.InsecureExporter = true
+		// No scheme: keep caller-provided InsecureExporter to avoid silent transport downgrade.
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ep := strings.TrimSpace(cfg.CollectorExporterEndpoint); ep != "" {
switch {
case strings.HasPrefix(ep, "http://"):
cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "http://")
cfg.InsecureExporter = true
case strings.HasPrefix(ep, "https://"):
cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "https://")
default:
// No scheme — assume insecure (common in k8s internal comms).
cfg.InsecureExporter = true
}
if ep := strings.TrimSpace(cfg.CollectorExporterEndpoint); ep != "" {
cfg.CollectorExporterEndpoint = ep
switch {
case strings.HasPrefix(ep, "http://"):
cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "http://")
cfg.InsecureExporter = true
case strings.HasPrefix(ep, "https://"):
cfg.CollectorExporterEndpoint = strings.TrimPrefix(ep, "https://")
default:
// No scheme: keep caller-provided InsecureExporter to avoid silent transport downgrade.
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commons/opentelemetry/otel.go` around lines 104 - 114, The endpoint
normalization currently trims into a local ep but fails to persist the trimmed
value for the schemeless branch and unconditionally sets
cfg.InsecureExporter=true for schemeless endpoints, risking a silent TLS
downgrade; update the switch in the endpoint handling (the block that inspects
ep and sets cfg.CollectorExporterEndpoint and cfg.InsecureExporter) so that you
always write the trimmed ep back into cfg.CollectorExporterEndpoint, set
cfg.InsecureExporter=true only when the scheme is explicitly "http://" and
ensure "https://" sets or leaves InsecureExporter=false; for the default (no
scheme) branch do not change cfg.InsecureExporter (just persist the trimmed
endpoint) so TLS behavior isn’t downgraded implicitly.

@gandalf-at-lerian gandalf-at-lerian merged commit 83cc99c into develop Mar 19, 2026
3 checks passed
@gandalf-at-lerian gandalf-at-lerian deleted the fix/otel-endpoint-url-normalization branch March 19, 2026 03:43
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.

1 participant