Skip to content

Commit 96ba312

Browse files
authored
fix: Use HTTP_PROXY and HTTPS_PROXY environment variables for proxy (#2397)
1 parent 8515dbb commit 96ba312

File tree

3 files changed

+66
-71
lines changed

3 files changed

+66
-71
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,16 @@ export OPENSHIFT_PYTHON_WRAPPER_LOG_LEVEL=<LOG_LEVEL> # can be: "DEBUG", "INFO",
9696
## Proxy Enablement
9797

9898
This configuration allows the client to route traffic through a specified proxy server.
99-
It can be enabled via the environment variable `OPENSHIFT_PYTHON_WRAPPER_CLIENT_USE_PROXY`.
10099

101100
To enable proxy configuration for the client:
102101

103-
1. Set the environment variable `OPENSHIFT_PYTHON_WRAPPER_CLIENT_USE_PROXY=<any value>`
102+
1. Define either `HTTPS_PROXY` or `HTTP_PROXY` environment variable with your proxy URL:
104103

105-
2. Define either `HTTPS_PROXY` or `HTTP_PROXY` environment variable with your proxy URL:
106104
```bash
107105
export HTTPS_PROXY="http://proxy.example.com:8080"
108106
# or
109107
export HTTP_PROXY="http://proxy.example.com:8080"
110108
```
111-
If neither variable is set when proxy is enabled, a `ValueError` will be raised.
112109

113110
## Code check
114111

ocp_resources/resource.py

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,13 @@ def _get_api_version(dyn_client: DynamicClient, api_group: str, kind: str) -> st
7777

7878

7979
def get_client(
80-
config_file: str = "",
80+
config_file: str | None = None,
8181
config_dict: dict[str, Any] | None = None,
82-
context: str = "",
83-
**kwargs: Any,
82+
context: str | None = None,
83+
client_configuration: kubernetes.client.Configuration | None = None,
84+
persist_config: bool = True,
85+
temp_file_path: str | None = None,
86+
try_refresh_token: bool = True,
8487
) -> DynamicClient:
8588
"""
8689
Get a kubernetes client.
@@ -97,19 +100,31 @@ def get_client(
97100
config_file (str): path to a kubeconfig file.
98101
config_dict (dict): dict with kubeconfig configuration.
99102
context (str): name of the context to use.
103+
persist_config (bool): whether to persist config file.
104+
temp_file_path (str): path to a temporary kubeconfig file.
105+
try_refresh_token (bool): try to refresh token
100106
101107
Returns:
102108
DynamicClient: a kubernetes client.
103109
"""
110+
proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("HTTP_PROXY")
111+
112+
client_configuration = client_configuration or kubernetes.client.Configuration()
113+
114+
if not client_configuration.proxy and proxy:
115+
LOGGER.info(f"Setting proxy from environment variable: {proxy}")
116+
client_configuration.proxy = proxy
117+
104118
# Ref: https://github.com/kubernetes-client/python/blob/v26.1.0/kubernetes/base/config/kube_config.py
105119
if config_dict:
106-
return kubernetes.dynamic.DynamicClient(
107-
client=kubernetes.config.new_client_from_config_dict(
108-
config_dict=config_dict, context=context or None, **kwargs
109-
)
120+
_client = kubernetes.config.new_client_from_config_dict(
121+
config_dict=config_dict,
122+
context=context,
123+
client_configuration=client_configuration,
124+
persist_config=persist_config,
125+
temp_file_path=temp_file_path,
110126
)
111-
client_configuration = kwargs.get("client_configuration", kubernetes.client.Configuration())
112-
try:
127+
else:
113128
# Ref: https://github.com/kubernetes-client/python/blob/v26.1.0/kubernetes/base/config/__init__.py
114129
LOGGER.info("Trying to get client via new_client_from_config")
115130

@@ -118,36 +133,22 @@ def get_client(
118133
# is populated during import which comes before setting the variable in code.
119134
config_file = config_file or os.environ.get("KUBECONFIG", "~/.kube/config")
120135

121-
if os.environ.get("OPENSHIFT_PYTHON_WRAPPER_CLIENT_USE_PROXY"):
122-
proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("HTTP_PROXY")
123-
if not proxy:
124-
raise ValueError(
125-
"Proxy configuration is enabled but neither HTTPS_PROXY nor HTTP_PROXY environment variables are set."
126-
)
127-
if client_configuration.proxy and client_configuration.proxy != proxy:
128-
raise ValueError(
129-
f"Conflicting proxy settings: client_configuration.proxy={client_configuration.proxy}, "
130-
f"but the environment variable 'HTTPS_PROXY/HTTP_PROXY' defines proxy as {proxy}."
131-
)
132-
client_configuration.proxy = proxy
133-
134-
kwargs["client_configuration"] = client_configuration
135-
136-
return kubernetes.dynamic.DynamicClient(
137-
client=kubernetes.config.new_client_from_config(
138-
config_file=config_file,
139-
context=context or None,
140-
**kwargs,
141-
)
136+
_client = kubernetes.config.new_client_from_config(
137+
config_file=config_file,
138+
context=context,
139+
client_configuration=client_configuration,
140+
persist_config=persist_config,
142141
)
142+
143+
try:
144+
return kubernetes.dynamic.DynamicClient(client=_client)
143145
except MaxRetryError:
144146
# Ref: https://github.com/kubernetes-client/python/blob/v26.1.0/kubernetes/base/config/incluster_config.py
145147
LOGGER.info("Trying to get client via incluster_config")
146148
return kubernetes.dynamic.DynamicClient(
147149
client=kubernetes.config.incluster_config.load_incluster_config(
148-
client_configuration=client_configuration,
149-
try_refresh_token=kwargs.get("try_refresh_token", True),
150-
)
150+
client_configuration=client_configuration, try_refresh_token=try_refresh_token
151+
),
151152
)
152153

153154

@@ -429,9 +430,9 @@ def __init__(
429430
dry_run: bool = False,
430431
node_selector: dict[str, Any] | None = None,
431432
node_selector_labels: dict[str, str] | None = None,
432-
config_file: str = "",
433+
config_file: str | None = None,
433434
config_dict: dict[str, Any] | None = None,
434-
context: str = "",
435+
context: str | None = None,
435436
label: dict[str, str] | None = None,
436437
annotations: dict[str, str] | None = None,
437438
api_group: str = "",
@@ -486,11 +487,6 @@ def __init__(
486487
self.node_selector = node_selector
487488
self.node_selector_labels = node_selector_labels
488489
self.config_file = config_file
489-
if not isinstance(self.config_file, str):
490-
# If we pass config_file which isn't a string, get_client will fail and it will be very hard to know why.
491-
# Better fail here and let the user know.
492-
raise ValueError("config_file must be a string")
493-
494490
self.config_dict = config_dict or {}
495491
self.context = context
496492
self.label = label
@@ -967,10 +963,10 @@ def retry_cluster_exceptions(
967963
def get(
968964
cls,
969965
config_file: str = "",
970-
context: str = "",
971966
singular_name: str = "",
972967
exceptions_dict: dict[type[Exception], list[str]] = DEFAULT_CLUSTER_RETRY_EXCEPTIONS,
973968
raw: bool = False,
969+
context: str | None = None,
974970
dyn_client: DynamicClient | None = None,
975971
*args: Any,
976972
**kwargs: Any,
@@ -1196,7 +1192,7 @@ def events(
11961192
def get_all_cluster_resources(
11971193
client: DynamicClient | None = None,
11981194
config_file: str = "",
1199-
context: str = "",
1195+
context: str | None = None,
12001196
config_dict: dict[str, Any] | None = None,
12011197
*args: Any,
12021198
**kwargs: Any,
@@ -1333,10 +1329,10 @@ def __init__(
13331329
def get(
13341330
cls,
13351331
config_file: str = "",
1336-
context: str = "",
13371332
singular_name: str = "",
13381333
exceptions_dict: dict[type[Exception], list[str]] = DEFAULT_CLUSTER_RETRY_EXCEPTIONS,
13391334
raw: bool = False,
1335+
context: str | None = None,
13401336
dyn_client: DynamicClient | None = None,
13411337
*args: Any,
13421338
**kwargs: Any,

tests/test_resources.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
from __future__ import annotations
2+
13
import pytest
24
import yaml
35
from docker.errors import DockerException
4-
import kubernetes
56
from testcontainers.k3s import K3SContainer
67

78
from ocp_resources.exceptions import ResourceTeardownError
@@ -19,13 +20,18 @@ def clean_up(self, wait: bool = True, timeout: int | None = None) -> bool:
1920
return False
2021

2122

22-
@pytest.fixture(scope="session")
23-
def client():
23+
@pytest.fixture(scope="class")
24+
def k3scontainer_config():
2425
try:
2526
with K3SContainer() as k3s:
26-
yield get_client(config_dict=yaml.safe_load(k3s.config_yaml()))
27-
except DockerException:
28-
pytest.skip("K3S container not available")
27+
yield yaml.safe_load(k3s.config_yaml())
28+
except DockerException as ex:
29+
pytest.skip(f"K3S container not available. {ex}")
30+
31+
32+
@pytest.fixture(scope="class")
33+
def client(k3scontainer_config):
34+
yield get_client(config_dict=k3scontainer_config)
2935

3036

3137
@pytest.fixture(scope="class")
@@ -109,25 +115,21 @@ def test_resource_context_manager_exit(self, client):
109115
with SecretTestExit(name="test-context-manager-exit", namespace="default", client=client):
110116
pass
111117

112-
def test_proxy_enabled_but_no_proxy_set(self, monkeypatch):
113-
monkeypatch.setenv(name="OPENSHIFT_PYTHON_WRAPPER_CLIENT_USE_PROXY", value="1")
114118

115-
with pytest.raises(
116-
ValueError,
117-
match="Proxy configuration is enabled but neither HTTPS_PROXY nor HTTP_PROXY environment variables are set.",
118-
):
119-
get_client()
119+
class TestClient:
120+
def test_client_with_proxy(self, monkeypatch, k3scontainer_config):
121+
http_proxy = "http://env-http-proxy.com"
122+
monkeypatch.setenv(name="HTTPS_PROXY", value=http_proxy)
120123

121-
def test_proxy_conflict_raises_value_error(self, monkeypatch):
122-
monkeypatch.setenv(name="OPENSHIFT_PYTHON_WRAPPER_CLIENT_USE_PROXY", value="1")
123-
monkeypatch.setenv(name="HTTPS_PROXY", value="http://env-proxy.com")
124+
client = get_client(config_dict=k3scontainer_config)
125+
assert client.configuration.proxy == http_proxy
124126

125-
client_configuration = kubernetes.client.Configuration()
126-
client_configuration.proxy = "http://not-env-proxy.com"
127+
def test_proxy_precedence(self, monkeypatch, k3scontainer_config):
128+
https_proxy = "https://env-https-proxy.com"
129+
http_proxy = "http://env-http-proxy.com"
130+
monkeypatch.setenv(name="HTTPS_PROXY", value=https_proxy)
131+
monkeypatch.setenv(name="HTTP_PROXY", value=http_proxy)
127132

128-
with pytest.raises(
129-
ValueError,
130-
match="Conflicting proxy settings: client_configuration.proxy=http://not-env-proxy.com, "
131-
"but the environment variable 'HTTPS_PROXY/HTTP_PROXY' defines proxy as http://env-proxy.com.",
132-
):
133-
get_client(client_configuration=client_configuration)
133+
client = get_client(config_dict=k3scontainer_config)
134+
# Verify HTTPS_PROXY takes precedence over HTTP_PROXY
135+
assert client.configuration.proxy == https_proxy

0 commit comments

Comments
 (0)