From 007729b973c24c6597e6ca4c8285295c36a8dafb Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 23 Jun 2026 10:55:44 +0200 Subject: [PATCH 1/7] chore: don't reset default proxy settings Signed-off-by: Anatolii Bazko --- controllers/workspace/http.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/controllers/workspace/http.go b/controllers/workspace/http.go index 82d77df35..dc6bf509f 100644 --- a/controllers/workspace/http.go +++ b/controllers/workspace/http.go @@ -122,7 +122,10 @@ func (h *DefaultHttpClientsFactory) GetHttpClient(ctx context.Context, routingCo func (h *DefaultHttpClientsFactory) createHttpClient(routingConfig *controller.RoutingConfig, certsCM *corev1.ConfigMap) *http.Client { transport := http.DefaultTransport.(*http.Transport).Clone() - transport.Proxy = h.getProxyFunc(routingConfig) + proxyFunc := h.getProxyFunc(routingConfig) + if proxyFunc != nil { + transport.Proxy = proxyFunc + } transport.TLSClientConfig = &tls.Config{ RootCAs: h.getCaCertPool(certsCM), } @@ -199,7 +202,10 @@ func (h *DefaultHttpClientsFactory) shouldCreateHealthCheckHttpClient(routingCon func (h *DefaultHttpClientsFactory) createHealthCheckHttpClient(routingConfig *controller.RoutingConfig) *http.Client { transport := http.DefaultTransport.(*http.Transport).Clone() - transport.Proxy = h.getProxyFunc(routingConfig) + proxyFunc := h.getProxyFunc(routingConfig) + if proxyFunc != nil { + transport.Proxy = proxyFunc + } transport.TLSClientConfig = &tls.Config{ InsecureSkipVerify: true, } From 814ecf4122ddd150f37f7918366d01e4a330d48e Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 23 Jun 2026 10:56:29 +0200 Subject: [PATCH 2/7] chore: don't reset default proxy settings Signed-off-by: Anatolii Bazko --- controllers/workspace/http.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controllers/workspace/http.go b/controllers/workspace/http.go index dc6bf509f..a1b2c890e 100644 --- a/controllers/workspace/http.go +++ b/controllers/workspace/http.go @@ -124,6 +124,7 @@ func (h *DefaultHttpClientsFactory) createHttpClient(routingConfig *controller.R transport := http.DefaultTransport.(*http.Transport).Clone() proxyFunc := h.getProxyFunc(routingConfig) if proxyFunc != nil { + // If Proxy is nil or returns a nil *URL, no proxy is used. transport.Proxy = proxyFunc } transport.TLSClientConfig = &tls.Config{ @@ -204,6 +205,7 @@ func (h *DefaultHttpClientsFactory) createHealthCheckHttpClient(routingConfig *c transport := http.DefaultTransport.(*http.Transport).Clone() proxyFunc := h.getProxyFunc(routingConfig) if proxyFunc != nil { + // If Proxy is nil or returns a nil *URL, no proxy is used. transport.Proxy = proxyFunc } transport.TLSClientConfig = &tls.Config{ From b090a1681c1936959254bb61cf6fac33b4e3927d Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 23 Jun 2026 11:16:17 +0200 Subject: [PATCH 3/7] chore: don't reset default proxy settings Signed-off-by: Anatolii Bazko --- controllers/workspace/http.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/controllers/workspace/http.go b/controllers/workspace/http.go index a1b2c890e..e8d7c9df0 100644 --- a/controllers/workspace/http.go +++ b/controllers/workspace/http.go @@ -226,6 +226,13 @@ func (h *DefaultHttpClientsFactory) getProxyFunc(routingConfig *controller.Routi return nil } + // Since routingConfig is the result of merging the global configuration with + // the workspace configuration, we need an additional check to avoid accidentally + // resetting the proxy configuration. + if *routingConfig.ProxyConfig.HttpProxy == "" || *routingConfig.ProxyConfig.HttpsProxy == "" { + return nil + } + proxyConfig := httpproxy.Config{} if routingConfig.ProxyConfig.HttpProxy != nil { proxyConfig.HTTPProxy = *routingConfig.ProxyConfig.HttpProxy From 03545e92867a4db5dc1d93325d97c34a38c1f512 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 23 Jun 2026 11:40:48 +0200 Subject: [PATCH 4/7] chore: don't reset default proxy settings Signed-off-by: Anatolii Bazko --- controllers/workspace/http.go | 14 +++++++------- controllers/workspace/http_test.go | 13 ++++++++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/controllers/workspace/http.go b/controllers/workspace/http.go index e8d7c9df0..3143b7bf3 100644 --- a/controllers/workspace/http.go +++ b/controllers/workspace/http.go @@ -226,13 +226,6 @@ func (h *DefaultHttpClientsFactory) getProxyFunc(routingConfig *controller.Routi return nil } - // Since routingConfig is the result of merging the global configuration with - // the workspace configuration, we need an additional check to avoid accidentally - // resetting the proxy configuration. - if *routingConfig.ProxyConfig.HttpProxy == "" || *routingConfig.ProxyConfig.HttpsProxy == "" { - return nil - } - proxyConfig := httpproxy.Config{} if routingConfig.ProxyConfig.HttpProxy != nil { proxyConfig.HTTPProxy = *routingConfig.ProxyConfig.HttpProxy @@ -244,6 +237,13 @@ func (h *DefaultHttpClientsFactory) getProxyFunc(routingConfig *controller.Routi proxyConfig.NoProxy = *routingConfig.ProxyConfig.NoProxy } + // Since routingConfig is the result of merging the global configuration with + // the workspace configuration, we need an additional check to avoid accidentally + // resetting the proxy configuration. + if proxyConfig.HTTPProxy == "" || proxyConfig.HTTPSProxy == "" { + return nil + } + return func(req *http.Request) (*url.URL, error) { return proxyConfig.ProxyFunc()(req.URL) } diff --git a/controllers/workspace/http_test.go b/controllers/workspace/http_test.go index c1fd05786..a0a2fbdf1 100644 --- a/controllers/workspace/http_test.go +++ b/controllers/workspace/http_test.go @@ -152,12 +152,17 @@ func runCommonClientTests(t *testing.T, getClient getClientFunc) { client3 := getClient(factory, nil) assert.NotSame(t, client2, client3) - assert.Nil(t, client3.Transport.(*http.Transport).Proxy) + + // Default proxy config is not nil + assert.NotNil(t, client3.Transport.(*http.Transport).Proxy) }) t.Run("safe for concurrent access", func(t *testing.T) { factory := newTestFactory(t) - routingConfigs := []*controller.RoutingConfig{nil, routingConfigWithProxy("http://proxy:80", "", "")} + routingConfigs := []*controller.RoutingConfig{ + routingConfigWithProxy("http://proxy:80", "", ""), + routingConfigWithProxy("http://proxy:90", "", ""), + } var wg sync.WaitGroup for i := 0; i < 50; i++ { @@ -169,9 +174,7 @@ func runCommonClientTests(t *testing.T, getClient getClientFunc) { client := getClient(factory, routingConfig) assert.NotNil(t, client) - if routingConfig != nil { - assert.NotNil(t, client.Transport.(*http.Transport).Proxy) - } + assert.NotNil(t, client.Transport.(*http.Transport).Proxy) }(i) } wg.Wait() From 80de25d3ad5164a2175c93289e07c7ad618dbf54 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 23 Jun 2026 17:19:01 +0200 Subject: [PATCH 5/7] chore: don't reset default proxy settings Signed-off-by: Anatolii Bazko --- controllers/workspace/http.go | 126 +++++++++---------------- controllers/workspace/http_test.go | 142 +++++++++++++++-------------- controllers/workspace/status.go | 2 +- 3 files changed, 118 insertions(+), 152 deletions(-) diff --git a/controllers/workspace/http.go b/controllers/workspace/http.go index 3143b7bf3..f08f5fdef 100644 --- a/controllers/workspace/http.go +++ b/controllers/workspace/http.go @@ -25,6 +25,7 @@ import ( "time" controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" + "github.com/devfile/devworkspace-operator/pkg/config" "k8s.io/apimachinery/pkg/types" "github.com/go-logr/logr" @@ -44,7 +45,7 @@ type HttpClientsFactory interface { // GetHealthCheckHttpClient returns an HTTP client that skips TLS verification. // This client MUST only be used for workspace health/readiness checks, not for // fetching external content or making security-sensitive requests. - GetHealthCheckHttpClient(*controller.RoutingConfig) *http.Client + GetHealthCheckHttpClient() *http.Client } // DefaultHttpClientsFactory is a thread-safe, caching implementation of HttpClientsFactory. @@ -63,9 +64,8 @@ type DefaultHttpClientsFactory struct { httpClientConfigmapRef *controller.ConfigmapReference httpClientCertsVersion string - healthCheckHttpClientProxyConfig *controller.Proxy - systemCertPool *x509.CertPool + proxyFunc func(*http.Request) (*url.URL, error) } func SetupHttpClientsFactory(k8s client.Client, logger logr.Logger) error { @@ -74,10 +74,26 @@ func SetupHttpClientsFactory(k8s client.Client, logger logr.Logger) error { return fmt.Errorf("failed to load system cert pool: %w", err) } + proxyFunc := getProxyFunc() + + healthCheckHttpClientTransport := http.DefaultTransport.(*http.Transport).Clone() + if proxyFunc != nil { + // Preserve the default proxy (from env vars) when no explicit proxy is configured. + healthCheckHttpClientTransport.Proxy = proxyFunc + } + healthCheckHttpClientTransport.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, + } + httpClientsFactory = &DefaultHttpClientsFactory{ k8s: k8s, logger: logger, systemCertPool: systemCertPool, + proxyFunc: proxyFunc, + healthCheckHttpClient: &http.Client{ + Transport: healthCheckHttpClientTransport, + Timeout: 500 * time.Millisecond, + }, } return nil @@ -97,7 +113,7 @@ func (h *DefaultHttpClientsFactory) GetHttpClient(ctx context.Context, routingCo defer h.mu.Unlock() if h.shouldCreateHttpClient(routingConfig, certsCM) { - h.httpClient = h.createHttpClient(routingConfig, certsCM) + h.httpClient = h.createHttpClient(certsCM) if routingConfig == nil { h.httpClientProxyConfig = nil @@ -120,12 +136,11 @@ func (h *DefaultHttpClientsFactory) GetHttpClient(ctx context.Context, routingCo return h.httpClient } -func (h *DefaultHttpClientsFactory) createHttpClient(routingConfig *controller.RoutingConfig, certsCM *corev1.ConfigMap) *http.Client { +func (h *DefaultHttpClientsFactory) createHttpClient(certsCM *corev1.ConfigMap) *http.Client { transport := http.DefaultTransport.(*http.Transport).Clone() - proxyFunc := h.getProxyFunc(routingConfig) - if proxyFunc != nil { - // If Proxy is nil or returns a nil *URL, no proxy is used. - transport.Proxy = proxyFunc + if h.proxyFunc != nil { + // Preserve the default proxy (from env vars) when no explicit proxy is configured. + transport.Proxy = h.proxyFunc } transport.TLSClientConfig = &tls.Config{ RootCAs: h.getCaCertPool(certsCM), @@ -163,90 +178,35 @@ func (h *DefaultHttpClientsFactory) shouldCreateHttpClient(routingConfig *contro !reflect.DeepEqual(proxyConfig, h.httpClientProxyConfig) } -func (h *DefaultHttpClientsFactory) GetHealthCheckHttpClient(routingConfig *controller.RoutingConfig) *http.Client { +func (h *DefaultHttpClientsFactory) GetHealthCheckHttpClient() *http.Client { h.mu.RLock() - if !h.shouldCreateHealthCheckHttpClient(routingConfig) { - defer h.mu.RUnlock() - return h.healthCheckHttpClient - } - h.mu.RUnlock() - - h.mu.Lock() - defer h.mu.Unlock() - - if h.shouldCreateHealthCheckHttpClient(routingConfig) { - h.healthCheckHttpClient = h.createHealthCheckHttpClient(routingConfig) - - if routingConfig == nil { - h.healthCheckHttpClientProxyConfig = nil - } else { - h.healthCheckHttpClientProxyConfig = routingConfig.ProxyConfig.DeepCopy() - } - } + defer h.mu.RUnlock() return h.healthCheckHttpClient } -func (h *DefaultHttpClientsFactory) shouldCreateHealthCheckHttpClient(routingConfig *controller.RoutingConfig) bool { - if h.healthCheckHttpClient == nil { - return true - } - - var proxyConfig *controller.Proxy - - if routingConfig != nil { - proxyConfig = routingConfig.ProxyConfig - } - - return !reflect.DeepEqual(proxyConfig, h.healthCheckHttpClientProxyConfig) -} - -func (h *DefaultHttpClientsFactory) createHealthCheckHttpClient(routingConfig *controller.RoutingConfig) *http.Client { - transport := http.DefaultTransport.(*http.Transport).Clone() - proxyFunc := h.getProxyFunc(routingConfig) - if proxyFunc != nil { - // If Proxy is nil or returns a nil *URL, no proxy is used. - transport.Proxy = proxyFunc - } - transport.TLSClientConfig = &tls.Config{ - InsecureSkipVerify: true, - } - - return &http.Client{ - Transport: transport, - Timeout: 500 * time.Millisecond, - } -} - // getProxyFunc returns a proxy function based on the proxy settings in routingConfig. -// Returns nil if no proxy is configured; a nil proxy func causes the HTTP transport to -// use the default proxy settings from environment variables. -func (h *DefaultHttpClientsFactory) getProxyFunc(routingConfig *controller.RoutingConfig) func(*http.Request) (*url.URL, error) { - if routingConfig == nil || routingConfig.ProxyConfig == nil { - return nil - } +func getProxyFunc() func(*http.Request) (*url.URL, error) { + globalConfig := config.GetGlobalConfig() - proxyConfig := httpproxy.Config{} - if routingConfig.ProxyConfig.HttpProxy != nil { - proxyConfig.HTTPProxy = *routingConfig.ProxyConfig.HttpProxy - } - if routingConfig.ProxyConfig.HttpsProxy != nil { - proxyConfig.HTTPSProxy = *routingConfig.ProxyConfig.HttpsProxy - } - if routingConfig.ProxyConfig.NoProxy != nil { - proxyConfig.NoProxy = *routingConfig.ProxyConfig.NoProxy - } + if globalConfig.Routing != nil && globalConfig.Routing.ProxyConfig != nil { + proxyConf := httpproxy.Config{} + if globalConfig.Routing.ProxyConfig.HttpProxy != nil { + proxyConf.HTTPProxy = *globalConfig.Routing.ProxyConfig.HttpProxy + } + if globalConfig.Routing.ProxyConfig.HttpsProxy != nil { + proxyConf.HTTPSProxy = *globalConfig.Routing.ProxyConfig.HttpsProxy + } + if globalConfig.Routing.ProxyConfig.NoProxy != nil { + proxyConf.NoProxy = *globalConfig.Routing.ProxyConfig.NoProxy + } - // Since routingConfig is the result of merging the global configuration with - // the workspace configuration, we need an additional check to avoid accidentally - // resetting the proxy configuration. - if proxyConfig.HTTPProxy == "" || proxyConfig.HTTPSProxy == "" { - return nil + return func(req *http.Request) (*url.URL, error) { + return proxyConf.ProxyFunc()(req.URL) + } } - return func(req *http.Request) (*url.URL, error) { - return proxyConfig.ProxyFunc()(req.URL) - } + return nil } // getCaCertPool returns a CA cert pool that includes system certs and any additional certs from the ConfigMap. diff --git a/controllers/workspace/http_test.go b/controllers/workspace/http_test.go index a0a2fbdf1..99e786cdc 100644 --- a/controllers/workspace/http_test.go +++ b/controllers/workspace/http_test.go @@ -18,6 +18,7 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -48,7 +49,7 @@ func (t *TestHttpClientsFactory) GetHttpClient(_ context.Context, _ *controller. return t.client } -func (t *TestHttpClientsFactory) GetHealthCheckHttpClient(_ *controller.RoutingConfig) *http.Client { +func (t *TestHttpClientsFactory) GetHealthCheckHttpClient() *http.Client { return t.healthCheckHttpClient } @@ -59,17 +60,45 @@ func SetupHttpClientsForTesting(client *http.Client) { } } -type getClientFunc func(factory HttpClientsFactory, routingConfig *controller.RoutingConfig) *http.Client - func TestGetHttpClient(t *testing.T) { - getClient := func( - f HttpClientsFactory, - routingConfig *controller.RoutingConfig, - ) *http.Client { - return f.GetHttpClient(context.Background(), routingConfig) - } + t.Run("returns non-nil client", func(t *testing.T) { + factory := newTestFactory(t) + + client := factory.GetHttpClient(context.Background(), nil) + + require.NotNil(t, client) + }) + + t.Run("caches client on repeated calls", func(t *testing.T) { + factory := newTestFactory(t) + + client1 := factory.GetHttpClient(context.Background(), nil) + client2 := factory.GetHttpClient(context.Background(), nil) + + assert.Same(t, client1, client2) + }) + + t.Run("rebuilds client when proxy changes", func(t *testing.T) { + factory := newTestFactory(t) + routingConfig1 := routingConfigWithProxy("http://proxy:80", "", "") + routingConfig2 := routingConfigWithProxy("http://proxy:90", "", "") + + client1 := factory.GetHttpClient(context.Background(), routingConfig1) + + assert.NotNil(t, client1.Transport.(*http.Transport).Proxy) + + client2 := factory.GetHttpClient(context.Background(), routingConfig2) + + assert.NotNil(t, client2.Transport.(*http.Transport).Proxy) + assert.NotSame(t, client1, client2) - runCommonClientTests(t, getClient) + client3 := factory.GetHttpClient(context.Background(), nil) + + assert.NotSame(t, client2, client3) + + // Default proxy config is not nil as well + assert.NotNil(t, client3.Transport.(*http.Transport).Proxy) + }) t.Run("rebuilds client when certs changes", func(t *testing.T) { factory := newTestFactory(t, @@ -104,64 +133,27 @@ func TestGetHttpClient(t *testing.T) { assert.NotSame(t, client2, client3) assert.Nil(t, client3.Transport.(*http.Transport).TLSClientConfig.RootCAs) }) -} - -func TestGetHealthCheckHttpClient(t *testing.T) { - getClient := func( - f HttpClientsFactory, - rc *controller.RoutingConfig, - ) *http.Client { - return f.GetHealthCheckHttpClient(rc) - } - - runCommonClientTests(t, getClient) -} - -func runCommonClientTests(t *testing.T, getClient getClientFunc) { - t.Run("returns non-nil client", func(t *testing.T) { - factory := newTestFactory(t) - - client := getClient(factory, nil) - - require.NotNil(t, client) - }) - - t.Run("caches client on repeated calls", func(t *testing.T) { - factory := newTestFactory(t) - - client1 := getClient(factory, nil) - client2 := getClient(factory, nil) - - assert.Same(t, client1, client2) - }) - - t.Run("rebuilds client when proxy changes", func(t *testing.T) { - factory := newTestFactory(t) - routingConfig1 := routingConfigWithProxy("http://proxy:80", "", "") - routingConfig2 := routingConfigWithProxy("http://proxy:90", "", "") - - client1 := getClient(factory, routingConfig1) - - assert.NotNil(t, client1.Transport.(*http.Transport).Proxy) - - client2 := getClient(factory, routingConfig2) - - assert.NotNil(t, client2.Transport.(*http.Transport).Proxy) - assert.NotSame(t, client1, client2) - - client3 := getClient(factory, nil) - - assert.NotSame(t, client2, client3) - - // Default proxy config is not nil - assert.NotNil(t, client3.Transport.(*http.Transport).Proxy) - }) t.Run("safe for concurrent access", func(t *testing.T) { - factory := newTestFactory(t) + factory := newTestFactory(t, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-certs-1", + Namespace: "default", + }, + Data: map[string]string{"ca.crt": generateTestCACert(t)}, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-certs-2", + Namespace: "default", + }, + Data: map[string]string{"ca.crt": generateTestCACert(t)}, + }) routingConfigs := []*controller.RoutingConfig{ - routingConfigWithProxy("http://proxy:80", "", ""), - routingConfigWithProxy("http://proxy:90", "", ""), + nil, + routingConfigWithCerts("test-certs-1", "default"), + routingConfigWithCerts("test-certs-2", "default"), } var wg sync.WaitGroup @@ -171,10 +163,15 @@ func runCommonClientTests(t *testing.T, getClient getClientFunc) { defer wg.Done() routingConfig := routingConfigs[idx%len(routingConfigs)] - client := getClient(factory, routingConfig) + client := factory.GetHttpClient(context.Background(), routingConfig) + + require.NotNil(t, client) - assert.NotNil(t, client) - assert.NotNil(t, client.Transport.(*http.Transport).Proxy) + tlsConfig := client.Transport.(*http.Transport).TLSClientConfig + require.NotNil(t, tlsConfig) + if routingConfig != nil && routingConfig.TLSCertificateConfigmapRef != nil { + assert.NotNil(t, tlsConfig.RootCAs) + } }(i) } wg.Wait() @@ -212,10 +209,19 @@ func newTestFactory(t *testing.T, objs ...runtime.Object) *DefaultHttpClientsFac k8sClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objs...).Build() + healthCheckTransport := http.DefaultTransport.(*http.Transport).Clone() + healthCheckTransport.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, + } + return &DefaultHttpClientsFactory{ k8s: k8sClient, logger: zap.New(zap.UseDevMode(true)), systemCertPool: systemCertPool, + healthCheckHttpClient: &http.Client{ + Transport: healthCheckTransport, + Timeout: 500 * time.Millisecond, + }, } } diff --git a/controllers/workspace/status.go b/controllers/workspace/status.go index 8cac692aa..c7b07a3fe 100644 --- a/controllers/workspace/status.go +++ b/controllers/workspace/status.go @@ -210,7 +210,7 @@ func checkServerStatus(workspace *common.DevWorkspaceWithConfig) (ok bool, respo } healthz.Path = path.Join(healthz.Path, "healthz") - healthCheckHttpClient := httpClientsFactory.GetHealthCheckHttpClient(workspace.Config.Routing) + healthCheckHttpClient := httpClientsFactory.GetHealthCheckHttpClient() resp, err := healthCheckHttpClient.Get(healthz.String()) if err != nil { return false, nil, &dwerrors.RetryError{Err: err, Message: "Failed to check server status", RequeueAfter: 1 * time.Second} From 84aa6b42132373fd5b8a0862582170d0ec8de365 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 23 Jun 2026 17:56:30 +0200 Subject: [PATCH 6/7] chore: don't reset default proxy settings Signed-off-by: Anatolii Bazko --- controllers/workspace/http.go | 6 +++++- controllers/workspace/http_test.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/controllers/workspace/http.go b/controllers/workspace/http.go index f08f5fdef..520461886 100644 --- a/controllers/workspace/http.go +++ b/controllers/workspace/http.go @@ -185,7 +185,7 @@ func (h *DefaultHttpClientsFactory) GetHealthCheckHttpClient() *http.Client { return h.healthCheckHttpClient } -// getProxyFunc returns a proxy function based on the proxy settings in routingConfig. +// getProxyFunc returns a proxy function. func getProxyFunc() func(*http.Request) (*url.URL, error) { globalConfig := config.GetGlobalConfig() @@ -201,6 +201,10 @@ func getProxyFunc() func(*http.Request) (*url.URL, error) { proxyConf.NoProxy = *globalConfig.Routing.ProxyConfig.NoProxy } + if proxyConf.HTTPProxy == "" && proxyConf.HTTPSProxy == "" { + return nil + } + return func(req *http.Request) (*url.URL, error) { return proxyConf.ProxyFunc()(req.URL) } diff --git a/controllers/workspace/http_test.go b/controllers/workspace/http_test.go index 99e786cdc..a38fff12c 100644 --- a/controllers/workspace/http_test.go +++ b/controllers/workspace/http_test.go @@ -60,6 +60,25 @@ func SetupHttpClientsForTesting(client *http.Client) { } } +func TestHealthCheckHttpClient(t *testing.T) { + t.Run("returns non-nil client", func(t *testing.T) { + factory := newTestFactory(t) + + client := factory.GetHealthCheckHttpClient() + + require.NotNil(t, client) + }) + + t.Run("caches client on repeated calls", func(t *testing.T) { + factory := newTestFactory(t) + + client1 := factory.GetHealthCheckHttpClient() + client2 := factory.GetHealthCheckHttpClient() + + assert.Same(t, client1, client2) + }) +} + func TestGetHttpClient(t *testing.T) { t.Run("returns non-nil client", func(t *testing.T) { factory := newTestFactory(t) From 8fb281f056478936a3326afb7d1e1166f6d33679 Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Tue, 23 Jun 2026 19:34:54 +0200 Subject: [PATCH 7/7] chore: consmetic changes Signed-off-by: Anatolii Bazko --- controllers/workspace/http.go | 22 ++++++---------------- controllers/workspace/http_test.go | 22 ---------------------- 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/controllers/workspace/http.go b/controllers/workspace/http.go index 520461886..65a4db090 100644 --- a/controllers/workspace/http.go +++ b/controllers/workspace/http.go @@ -60,7 +60,6 @@ type DefaultHttpClientsFactory struct { mu sync.RWMutex - httpClientProxyConfig *controller.Proxy httpClientConfigmapRef *controller.ConfigmapReference httpClientCertsVersion string @@ -115,12 +114,6 @@ func (h *DefaultHttpClientsFactory) GetHttpClient(ctx context.Context, routingCo if h.shouldCreateHttpClient(routingConfig, certsCM) { h.httpClient = h.createHttpClient(certsCM) - if routingConfig == nil { - h.httpClientProxyConfig = nil - } else { - h.httpClientProxyConfig = routingConfig.ProxyConfig.DeepCopy() - } - if certsCM == nil { h.httpClientCertsVersion = "" h.httpClientConfigmapRef = nil @@ -159,7 +152,6 @@ func (h *DefaultHttpClientsFactory) shouldCreateHttpClient(routingConfig *contro var certsVersion string var configmapRef *controller.ConfigmapReference - var proxyConfig *controller.Proxy if certsCM != nil { certsVersion = certsCM.ResourceVersion @@ -169,13 +161,8 @@ func (h *DefaultHttpClientsFactory) shouldCreateHttpClient(routingConfig *contro } } - if routingConfig != nil { - proxyConfig = routingConfig.ProxyConfig - } - return certsVersion != h.httpClientCertsVersion || - !reflect.DeepEqual(configmapRef, h.httpClientConfigmapRef) || - !reflect.DeepEqual(proxyConfig, h.httpClientProxyConfig) + !reflect.DeepEqual(configmapRef, h.httpClientConfigmapRef) } func (h *DefaultHttpClientsFactory) GetHealthCheckHttpClient() *http.Client { @@ -185,7 +172,9 @@ func (h *DefaultHttpClientsFactory) GetHealthCheckHttpClient() *http.Client { return h.healthCheckHttpClient } -// getProxyFunc returns a proxy function. +// getProxyFunc returns a proxy function based on the global operator configuration. +// Returns nil if no proxy is configured; a nil proxy func causes the HTTP transport to +// use the default proxy settings from environment variables. func getProxyFunc() func(*http.Request) (*url.URL, error) { globalConfig := config.GetGlobalConfig() @@ -205,8 +194,9 @@ func getProxyFunc() func(*http.Request) (*url.URL, error) { return nil } + proxyFn := proxyConf.ProxyFunc() return func(req *http.Request) (*url.URL, error) { - return proxyConf.ProxyFunc()(req.URL) + return proxyFn(req.URL) } } diff --git a/controllers/workspace/http_test.go b/controllers/workspace/http_test.go index a38fff12c..59eda013e 100644 --- a/controllers/workspace/http_test.go +++ b/controllers/workspace/http_test.go @@ -97,28 +97,6 @@ func TestGetHttpClient(t *testing.T) { assert.Same(t, client1, client2) }) - t.Run("rebuilds client when proxy changes", func(t *testing.T) { - factory := newTestFactory(t) - routingConfig1 := routingConfigWithProxy("http://proxy:80", "", "") - routingConfig2 := routingConfigWithProxy("http://proxy:90", "", "") - - client1 := factory.GetHttpClient(context.Background(), routingConfig1) - - assert.NotNil(t, client1.Transport.(*http.Transport).Proxy) - - client2 := factory.GetHttpClient(context.Background(), routingConfig2) - - assert.NotNil(t, client2.Transport.(*http.Transport).Proxy) - assert.NotSame(t, client1, client2) - - client3 := factory.GetHttpClient(context.Background(), nil) - - assert.NotSame(t, client2, client3) - - // Default proxy config is not nil as well - assert.NotNil(t, client3.Transport.(*http.Transport).Proxy) - }) - t.Run("rebuilds client when certs changes", func(t *testing.T) { factory := newTestFactory(t, &corev1.ConfigMap{