Skip to content

fix: preserve default proxy settings#1656

Merged
dkwon17 merged 7 commits into
mainfrom
fixproxyconfig
Jun 23, 2026
Merged

fix: preserve default proxy settings#1656
dkwon17 merged 7 commits into
mainfrom
fixproxyconfig

Conversation

@tolusha

@tolusha tolusha commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Preserve default proxy settings

What issues does this PR fix or reference?

{"level":"info","ts":"2026-06-23T07:31:51Z","logger":"controllers.DevWorkspace","msg":"Failed to check server status: Get \"https://eclipse-che.apps.che-airgap-v20.crw-qe.com/happypath-dev/quarkus-api-example-wgx6/3100/healthz\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)","Request.Namespace":"happypath-dev-che","Request.Name":"quarkus-api-example-wgx6","devworkspace_id":"workspace5e9f8620bd9f4773"}

fix #1650

Is it tested? How?

Manually on disconnected cluster

PR Checklist

  • E2E tests pass (when PR is ready, comment /test v8-devworkspace-operator-e2e, v8-che-happy-path to trigger)
    • v8-devworkspace-operator-e2e: DevWorkspace e2e test
    • v8-che-happy-path: Happy path for verification integration with Che

Summary by CodeRabbit

Bug Fixes

  • Fixed HTTP proxy handling so proxy behavior is computed once from global configuration and applied consistently, without overriding environment/default proxy behavior when no explicit proxy is set.
  • Improved health-check reliability and performance by using a cached, stable health-check HTTP client that isn’t impacted by per-workspace routing changes.

Tests

  • Refactored and expanded HTTP client tests, including new health-check coverage, stronger caching assertions, and expanded concurrency/TLS validation.

tolusha added 3 commits June 23, 2026 10:55
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd1057fa-6cba-4778-b0fb-90523a1c7194

📥 Commits

Reviewing files that changed from the base of the PR and between 80de25d and 8fb281f.

📒 Files selected for processing (2)
  • controllers/workspace/http.go
  • controllers/workspace/http_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • controllers/workspace/http.go
  • controllers/workspace/http_test.go

📝 Walkthrough

Walkthrough

HTTP client factory is refactored to decouple proxy configuration from per-workspace routing. Proxy behavior is now computed once globally during setup and stored on the factory; both regular and health-check clients use this single stored proxy function. The GetHealthCheckHttpClient method is simplified to take no parameters. Conditional proxy assignment is applied in client creation only when the stored proxy function is non-nil, allowing transports to fall back to default proxy behavior. Tests are updated to match the new parameterless API and expanded to validate TLS handling and concurrent access. The status check call site is updated to use the simplified API.

Changes

Proxy decoupling from routing config

Layer / File(s) Summary
Factory interface and global proxy setup
controllers/workspace/http.go
GetHealthCheckHttpClient interface method removes RoutingConfig parameter. DefaultHttpClientsFactory replaces per-routing proxy tracking with a single proxyFunc field. SetupHttpClientsFactory computes proxyFunc from global config, conditionally assigns it to the health-check transport, and caches healthCheckHttpClient.
Client creation and caching with stored proxy
controllers/workspace/http.go
GetHttpClient calls createHttpClient without RoutingConfig. createHttpClient conditionally sets transport proxy only when proxyFunc is non-nil. shouldCreateHttpClient no longer compares routing proxy state; rebuild decisions depend on cert ConfigMap only. GetHealthCheckHttpClient() returns cached instance. getProxyFunc() builds proxy from global settings and returns nil when unconfigured.
Test suite updates for new factory API
controllers/workspace/http_test.go
TestHttpClientsFactory.GetHealthCheckHttpClient becomes parameterless. TestHealthCheckHttpClient validates non-nil creation and caching. TestGetHttpClient uses inline subtests for creation, caching, and rebuild. Concurrent access test sets up two TLS cert ConfigMaps, creates many goroutines with varied routing inputs, and asserts TLS/RootCAs presence. newTestFactory explicitly constructs healthCheckHttpClient with TLS config and 500ms timeout.
Status check call site update
controllers/workspace/status.go
checkServerStatus updates health-check client call from routing-aware GetHealthCheckHttpClient(workspace.Config.Routing) to parameterless GetHealthCheckHttpClient().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Sequence Diagram

sequenceDiagram
  participant Init as Controller Init
  participant Factory as HttpClientsFactory
  participant GlobalConfig as Global Config
  participant Transport as HTTP Transport
  
  Init->>Factory: SetupHttpClientsFactory()
  Factory->>GlobalConfig: getProxyFunc reads global routing config
  GlobalConfig-->>Factory: HttpProxy, HttpsProxy, NoProxy values
  Factory->>Factory: Compute proxyFunc from global config
  alt proxyFunc is non-nil
    Factory->>Transport: Set Proxy field with proxyFunc
  else proxyFunc is nil
    Factory->>Transport: Leave Proxy field as default
  end
  Factory->>Factory: Create and cache healthCheckHttpClient
  
  Note over Factory: Later: Client requests
  participant Client as Caller
  Client->>Factory: GetHttpClient(routingConfig)
  Factory->>Factory: createHttpClient uses factory.proxyFunc
  Factory->>Transport: Conditionally assign Proxy
  Transport-->>Client: Return configured client
Loading

Possibly related PRs

  • devfile/devworkspace-operator#1650: Both PRs modify HttpClientsFactory health-check client initialization in SetupHttpClientsFactory and require corresponding updates to call sites, so the main PR's API signature refactor is directly entangled with the factory setup changes.

Suggested labels

lgtm, approved

Suggested reviewers

  • ibuziuk
  • akurinnoy
  • btjd
  • dkwon17
  • rohanKanojia

Poem

🐇 Through routing's tangled proxy vines,
I found a path of cleaner lines.
One config global, stored with care,
All clients breathe the same fresh air.
A simpler call, no routing passed—
The proxy's reign has breathed its last! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: modifying proxy behavior from per-workspace configuration to global operator configuration to preserve default proxy settings.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fixproxyconfig

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@tolusha

tolusha commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Hi! I'm che-ai-assistant — I help with your pull requests.

Available commands:

  • /che-ai-assistant generate-che-doc — Generate a documentation PR based on this PR's changes
  • /che-ai-assistant ok-pr-review — Run a comprehensive PR review (summary, code review, deep review, impact analysis)
  • /che-ai-assistant help — Show this help message

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controllers/workspace/http.go`:
- Around line 232-234: The condition checking ProxyConfig.HttpProxy and
ProxyConfig.HttpsProxy unconditionally dereferences these pointer fields without
first verifying they are not nil. Since both HttpProxy and HttpsProxy are
*string pointer fields with omitempty tag, they can legitimately be nil. Add
explicit nil-checks for both pointers before dereferencing them in the
condition, ensuring each pointer is not nil before accessing its value. Follow
the same pattern used in the existing code below at lines 237 and 240 which
already properly nil-check before dereferencing.
- Around line 232-234: In the proxy configuration validation check within the
ProxyConfig initialization, change the logical operator from `||` (OR) to `&&`
(AND) in the condition that evaluates *routingConfig.ProxyConfig.HttpProxy and
*routingConfig.ProxyConfig.HttpsProxy. This ensures the function only returns
nil when both proxy fields are empty, allowing configurations with at least one
proxy type set to be processed correctly instead of being skipped.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32ca01cb-c20c-44cf-be9d-63106cd185a6

📥 Commits

Reviewing files that changed from the base of the PR and between 3bbd19a and b090a16.

📒 Files selected for processing (1)
  • controllers/workspace/http.go

Comment thread controllers/workspace/http.go Outdated
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
@tolusha

tolusha commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/che-ai-assistant ok-pr-review

Review is complete. Please check the review comments below.

tolusha

This comment was marked as outdated.

Signed-off-by: Anatolii Bazko <abazko@redhat.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@controllers/workspace/http_test.go`:
- Around line 81-100: The test "rebuilds client when proxy changes" is not
actually exercising the proxyFunc field that createHttpClient now uses in
production, instead it relies on routingConfig.ProxyConfig which is ignored. To
fix this, either seed the proxyFunc field in newTestFactory with a test proxy
function that can be verified in assertions, or replace the assert.NotNil checks
on client.Transport.(*http.Transport).Proxy with assertions that verify the
actual proxy URL being returned by the transport matches what was configured in
the routingConfig passed to GetHttpClient.
- Around line 166-173: The require.NotNil assertions on the client and tlsConfig
variables are being called from within a worker goroutine, which is unsafe
because require.NotNil calls FailNow() and must run on the test goroutine only.
Create a channel to collect validation results or errors from the worker
goroutine, then move these require.NotNil assertions outside the goroutine to
execute after wg.Wait() completes so test failures are reported reliably on the
main test goroutine.

In `@controllers/workspace/http.go`:
- Around line 188-209: The getProxyFunc function currently returns a proxy
closure built from an empty httpproxy.Config when ProxyConfig exists but both
HttpProxy and HttpsProxy fields are nil, which overrides the default proxy
behavior. Add a guard condition before returning the proxy function to check
that at least one of HttpProxy or HttpsProxy is non-nil; only return the proxy
closure when at least one explicit proxy field is configured, otherwise return
nil to preserve the default proxy behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e20ed13a-ee18-4abc-9bfc-8c380ebdbcb2

📥 Commits

Reviewing files that changed from the base of the PR and between b090a16 and 80de25d.

📒 Files selected for processing (3)
  • controllers/workspace/http.go
  • controllers/workspace/http_test.go
  • controllers/workspace/status.go

Comment thread controllers/workspace/http_test.go Outdated
Comment thread controllers/workspace/http_test.go
Comment thread controllers/workspace/http.go Outdated
tolusha

This comment was marked as outdated.

Comment thread controllers/workspace/http.go
Comment thread controllers/workspace/http.go
Signed-off-by: Anatolii Bazko <abazko@redhat.com>
tolusha

This comment was marked as outdated.

Comment thread controllers/workspace/http.go Outdated
Comment thread controllers/workspace/http.go Outdated
Comment thread controllers/workspace/http_test.go Outdated
Comment thread controllers/workspace/http_test.go
Comment thread controllers/workspace/http.go
Comment thread controllers/workspace/http.go
Signed-off-by: Anatolii Bazko <abazko@redhat.com>

@tolusha tolusha left a comment

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.

Review Summary

This PR successfully fixes the proxy configuration bug on air-gapped clusters and significantly simplifies the HTTP client management code. The architectural decision to move from per-workspace to per-operator-lifecycle proxy configuration is sound.

Key Findings

Fix require.NotNil in concurrent test ()
The concurrent test uses require.NotNil(t, client) inside goroutines. require.NotNil calls t.FailNow() which invokes runtime.Goexit(). From a non-test goroutine, this doesn't propagate correctly to the test runner - it silently kills the goroutine without failing the test, potentially masking real failures. Should use assert.NotNil instead.

Document runtime proxy configuration behavior ()
The proxy function is computed once at operator startup. If an admin updates the DevWorkspaceOperatorConfig CR's proxy settings at runtime, the change won't take effect until the operator pod is restarted. Consider adding a code comment or documenting this in the operator configuration docs.

Clarify that routingConfig proxy fields are ignored ()
GetHttpClient still accepts *controller.RoutingConfig but only uses it for TLS certs - the ProxyConfig field is now silently ignored. Consider adding a comment clarifying that proxy config from routingConfig is intentionally ignored.

Add direct unit tests for getProxyFunc()
The getProxyFunc() function has multiple code paths (nil config, empty proxy URLs, HTTP-only, HTTPS-only, NoProxy-only) that aren't tested directly. Consider adding tests using config.SetGlobalConfigForTesting().

Verify NoProxy-only configuration ()
If only NoProxy is set (both HttpProxy and HttpsProxy are empty), getProxyFunc() returns nil, discarding the NoProxy setting. This is likely intentional but worth verifying against documented behavior.

Positive Observations

Excellent work on this fix:

  • Significant simplification: Removing ~40 lines of complex proxy-tracking logic
  • Correct nil-proxy semantics: Properly preserves Go's default http.ProxyFromEnvironment behavior
  • Performance improvement: Health check client uses only read locks, eliminating contention
  • Well-structured tests: Concurrency test exercises realistic cert-based variations

The architectural decision to move proxy configuration from per-workspace to per-operator-lifecycle is sound for this problem domain.


Generated by Claude Code PR Review

@tolusha tolusha left a comment

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.

Review Summary

This PR successfully fixes the proxy configuration bug on air-gapped clusters and significantly simplifies the HTTP client management code. The architectural decision to move from per-workspace to per-operator-lifecycle proxy configuration is sound.

Key Findings

Fix require.NotNil in concurrent test (http_test.go:163-164)
The concurrent test uses require.NotNil(t, client) inside goroutines. require.NotNil calls t.FailNow() which invokes runtime.Goexit(). From a non-test goroutine, this doesn't propagate correctly to the test runner - it silently kills the goroutine without failing the test, potentially masking real failures. Should use assert.NotNil instead.

Document runtime proxy configuration behavior (http.go:73-88)
The proxy function is computed once at operator startup. If an admin updates the DevWorkspaceOperatorConfig CR's proxy settings at runtime, the change won't take effect until the operator pod is restarted. Consider adding a code comment or documenting this in the operator configuration docs.

Clarify that routingConfig proxy fields are ignored (http.go:112-129)
GetHttpClient still accepts *controller.RoutingConfig but only uses it for TLS certs - the ProxyConfig field is now silently ignored. Consider adding a comment clarifying that proxy config from routingConfig is intentionally ignored.

Add direct unit tests for getProxyFunc()
The getProxyFunc() function has multiple code paths (nil config, empty proxy URLs, HTTP-only, HTTPS-only, NoProxy-only) that aren't tested directly. Consider adding tests using config.SetGlobalConfigForTesting().

Verify NoProxy-only configuration (http.go:202-204)
If only NoProxy is set (both HttpProxy and HttpsProxy are empty), getProxyFunc() returns nil, discarding the NoProxy setting. This is likely intentional but worth verifying against documented behavior.

Positive Observations

Excellent work on this fix:

  • Significant simplification: Removing ~40 lines of complex proxy-tracking logic
  • Correct nil-proxy semantics: Properly preserves Go's default http.ProxyFromEnvironment behavior
  • Performance improvement: Health check client uses only read locks, eliminating contention
  • Well-structured tests: Concurrency test exercises realistic cert-based variations

The architectural decision to move proxy configuration from per-workspace to per-operator-lifecycle is sound for this problem domain.


Generated by Claude Code PR Review

@openshift-ci

openshift-ci Bot commented Jun 23, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dkwon17, tolusha

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@dkwon17 dkwon17 merged commit af782ad into main Jun 23, 2026
12 checks passed
@dkwon17 dkwon17 deleted the fixproxyconfig branch June 23, 2026 20:05
@rohanKanojia

Copy link
Copy Markdown
Member

LGTM, thanks 👍

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants