Skip to content

Commit 51f4db5

Browse files
authored
Merge pull request #2390 from p172913/master
Changes made in configuration.py to accept environmental variables
2 parents 3e6cc58 + 945de4e commit 51f4db5

File tree

4 files changed

+204
-21
lines changed

4 files changed

+204
-21
lines changed

kubernetes/base/stream/ws_client_test.py

Lines changed: 118 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,70 @@
1717
from .ws_client import get_websocket_url
1818
from .ws_client import websocket_proxycare
1919
from kubernetes.client.configuration import Configuration
20+
import os
21+
import socket
22+
import threading
23+
import pytest
24+
from kubernetes import stream, client, config
2025

2126
try:
2227
import urllib3
2328
urllib3.disable_warnings()
2429
except ImportError:
2530
pass
31+
@pytest.fixture(autouse=True)
32+
def dummy_kubeconfig(tmp_path, monkeypatch):
33+
# Creating a kubeconfig
34+
content = """
35+
apiVersion: v1
36+
kind: Config
37+
clusters:
38+
- name: default
39+
cluster:
40+
server: http://127.0.0.1:8888
41+
contexts:
42+
- name: default
43+
context:
44+
cluster: default
45+
user: default
46+
users:
47+
- name: default
48+
user: {}
49+
current-context: default
50+
"""
51+
cfg_file = tmp_path / "kubeconfig"
52+
cfg_file.write_text(content)
53+
monkeypatch.setenv("KUBECONFIG", str(cfg_file))
2654

27-
def dictval(dict, key, default=None):
28-
try:
29-
val = dict[key]
30-
except KeyError:
31-
val = default
32-
return val
55+
56+
def dictval(dict_obj, key, default=None):
57+
58+
return dict_obj.get(key, default)
59+
60+
class DummyProxy(threading.Thread):
61+
"""
62+
A minimal HTTP proxy that flags any CONNECT request and returns 200 OK.
63+
Listens on 127.0.0.1:8888 by default.
64+
"""
65+
def __init__(self, host='127.0.0.1', port=8888):
66+
super().__init__(daemon=True)
67+
self.host = host
68+
self.port = port
69+
self.received_connect = False
70+
self._server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
71+
self._server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
72+
self._server_sock.bind((self.host, self.port))
73+
self._server_sock.listen(1)
74+
75+
def run(self):
76+
conn, _ = self._server_sock.accept()
77+
try:
78+
data = conn.recv(1024).decode('utf-8', errors='ignore')
79+
if data.startswith('CONNECT '):
80+
self.received_connect = True
81+
conn.sendall(b"HTTP/1.1 200 Connection established\r\n\r\n")
82+
finally:
83+
conn.close()
3384

3485
class WSClientTest(unittest.TestCase):
3586

@@ -56,21 +107,68 @@ def test_websocket_proxycare(self):
56107
( 'http://proxy.example.com:8080/', 'user:pass', '.example.com', 'proxy.example.com', 8080, ('user','pass'), ['.example.com']),
57108
( 'http://proxy.example.com:8080/', 'user:pass', 'localhost,.local,.example.com', 'proxy.example.com', 8080, ('user','pass'), ['localhost','.local','.example.com']),
58109
]:
59-
# setup input
60-
config = Configuration()
61-
if proxy is not None:
62-
setattr(config, 'proxy', proxy)
63-
if idpass is not None:
64-
setattr(config, 'proxy_headers', urllib3.util.make_headers(proxy_basic_auth=idpass))
110+
# input setup
111+
cfg = Configuration()
112+
if proxy:
113+
cfg.proxy = proxy
114+
if idpass:
115+
cfg.proxy_headers = urllib3.util.make_headers(proxy_basic_auth=idpass)
65116
if no_proxy is not None:
66-
setattr(config, 'no_proxy', no_proxy)
67-
# setup done
68-
# test starts
69-
connect_opt = websocket_proxycare( {}, config, None, None)
70-
self.assertEqual( dictval(connect_opt,'http_proxy_host'), expect_host)
71-
self.assertEqual( dictval(connect_opt,'http_proxy_port'), expect_port)
72-
self.assertEqual( dictval(connect_opt,'http_proxy_auth'), expect_auth)
73-
self.assertEqual( dictval(connect_opt,'http_no_proxy'), expect_noproxy)
117+
cfg.no_proxy = no_proxy
118+
119+
120+
connect_opts = websocket_proxycare({}, cfg, None, None)
121+
assert dictval(connect_opts, 'http_proxy_host') == expect_host
122+
assert dictval(connect_opts, 'http_proxy_port') == expect_port
123+
assert dictval(connect_opts, 'http_proxy_auth') == expect_auth
124+
assert dictval(connect_opts, 'http_no_proxy') == expect_noproxy
125+
126+
@pytest.fixture(scope="module")
127+
def dummy_proxy():
128+
#Dummy Proxy
129+
proxy = DummyProxy(port=8888)
130+
proxy.start()
131+
yield proxy
132+
133+
@pytest.fixture(autouse=True)
134+
def clear_proxy_env(monkeypatch):
135+
for var in ("HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy", "NO_PROXY", "no_proxy"):
136+
monkeypatch.delenv(var, raising=False)
137+
138+
def apply_proxy_to_conf():
139+
#apply HTTPS_PROXY env var and set it as global.
140+
cfg = client.Configuration.get_default_copy()
141+
cfg.proxy = os.getenv("HTTPS_PROXY")
142+
cfg.no_proxy = os.getenv("NO_PROXY", "")
143+
client.Configuration.set_default(cfg)
144+
145+
def test_rest_call_ignores_env(dummy_proxy, monkeypatch):
146+
# HTTPS_PROXY to dummy proxy
147+
monkeypatch.setenv("HTTPS_PROXY", "http://127.0.0.1:8888")
148+
# Avoid real HTTP request
149+
monkeypatch.setattr(client.CoreV1Api, "list_namespace", lambda self, *_args, **_kwargs: None)
150+
# Load config using kubeconfig
151+
config.load_kube_config(config_file=os.environ["KUBECONFIG"])
152+
apply_proxy_to_conf()
153+
# HTTPS_PROXY to dummy proxy
154+
monkeypatch.setenv("HTTPS_PROXY", "http://127.0.0.1:8888")
155+
config.load_kube_config(config_file=os.environ["KUBECONFIG"])
156+
apply_proxy_to_conf()
157+
v1 = client.CoreV1Api()
158+
v1.list_namespace(_preload_content=False)
159+
assert not dummy_proxy.received_connect, "REST path should ignore HTTPS_PROXY"
160+
161+
def test_websocket_call_honors_env(dummy_proxy, monkeypatch):
162+
# set HTTPS_PROXY again
163+
monkeypatch.setenv("HTTPS_PROXY", "http://127.0.0.1:8888")
164+
# Load kubeconfig
165+
config.load_kube_config(config_file=os.environ["KUBECONFIG"])
166+
apply_proxy_to_conf()
167+
opts = websocket_proxycare({}, client.Configuration.get_default_copy(), None, None)
168+
assert opts.get('http_proxy_host') == '127.0.0.1'
169+
assert opts.get('http_proxy_port') == 8888
170+
# Optionally verify no_proxy parsing
171+
assert opts.get('http_no_proxy') is None
74172

75173
if __name__ == '__main__':
76174
unittest.main()

kubernetes/client/configuration.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import multiprocessing
1818
import sys
1919
import urllib3
20+
import os
2021

2122
import six
2223
from six.moves import http_client as httplib
@@ -158,9 +159,15 @@ def __init__(self, host="http://localhost",
158159
"""
159160

160161
self.proxy = None
162+
if(os.getenv("HTTPS_PROXY")):self.proxy=os.getenv("HTTPS_PROXY")
163+
if(os.getenv("https_proxy")):self.proxy=os.getenv("https_proxy")
164+
if(os.getenv("HTTP_PROXY")):self.proxy=os.getenv("HTTP_PROXY")
165+
if(os.getenv("http_proxy")):self.proxy=os.getenv("http_proxy")
161166
"""Proxy URL
162167
"""
163168
self.no_proxy = None
169+
if(os.getenv("NO_PROXY")):self.no_proxy=os.getenv("NO_PROXY")
170+
if(os.getenv("no_proxy")):self.no_proxy=os.getenv("no_proxy")
164171
"""bypass proxy for host in the no_proxy list.
165172
"""
166173
self.proxy_headers = None

scripts/insert_proxy_config.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/usr/bin/env bash
2+
# insert_proxy_config.sh - run this after openapi-generator release.sh
3+
CONFIG_PATH="../python_kubernetes/kubernetes/client"
4+
5+
# Compute the full file path
6+
CONFIG_FILE="$CONFIG_PATH/configuration.py"
7+
8+
# --- Normalize Windows-style backslashes to Unix forward slashes ---
9+
CONFIG_FILE="$(echo "$CONFIG_FILE" | sed 's|\\|/|g')"
10+
11+
# --- Ensure the target file exists and is writable ---
12+
if [ ! -f "$CONFIG_FILE" ] || [ ! -w "$CONFIG_FILE" ]; then
13+
echo "Error: $CONFIG_FILE does not exist or is not writable." >&2
14+
exit 1
15+
fi
16+
17+
# --- Step 1: Ensure 'import os' follows existing imports (idempotent) ---
18+
if ! grep -qE '^import os$' "$CONFIG_FILE"; then
19+
LAST_IMPORT=$(grep -nE '^(import |from )' "$CONFIG_FILE" | tail -n1 | cut -d: -f1)
20+
if [ -n "$LAST_IMPORT" ]; then
21+
sed -i "$((LAST_IMPORT+1))i import os" "$CONFIG_FILE"
22+
else
23+
if head -n1 "$CONFIG_FILE" | grep -q '^#!'; then
24+
sed -i '2i import os' "$CONFIG_FILE"
25+
else
26+
sed -i '1i import os' "$CONFIG_FILE"
27+
fi
28+
fi
29+
echo "Inserted 'import os' after existing imports in $CONFIG_FILE."
30+
else
31+
echo "'import os' already present; no changes made."
32+
fi
33+
34+
# --- Step 2: Insert proxy & no_proxy environment code ---
35+
if ! grep -q 'os.getenv("HTTPS_PROXY"' "$CONFIG_FILE"; then
36+
PROXY_LINE=$(grep -n "self.proxy = None" "$CONFIG_FILE" | cut -d: -f1)
37+
NO_PROXY_LINE=$(grep -n "^[[:space:]]*self\.no_proxy[[:space:]]*=[[:space:]]*None" "$CONFIG_FILE" | cut -d: -f1)
38+
39+
if [ -n "$PROXY_LINE" ]; then
40+
INDENT=$(sed -n "${PROXY_LINE}s/^\( *\).*/\1/p" "$CONFIG_FILE")
41+
42+
BLOCK=""
43+
44+
if [ -z "$NO_PROXY_LINE" ]; then
45+
# self.no_proxy = None is not present → insert full block after self.proxy = None
46+
BLOCK+="${INDENT}# Load proxy from environment variables (if set)\n"
47+
BLOCK+="${INDENT}if os.getenv(\"HTTPS_PROXY\"): self.proxy = os.getenv(\"HTTPS_PROXY\")\n"
48+
BLOCK+="${INDENT}if os.getenv(\"https_proxy\"): self.proxy = os.getenv(\"https_proxy\")\n"
49+
BLOCK+="${INDENT}if os.getenv(\"HTTP_PROXY\"): self.proxy = os.getenv(\"HTTP_PROXY\")\n"
50+
BLOCK+="${INDENT}if os.getenv(\"http_proxy\"): self.proxy = os.getenv(\"http_proxy\")\n"
51+
BLOCK+="${INDENT}self.no_proxy = None\n"
52+
BLOCK+="${INDENT}# Load no_proxy from environment variables (if set)\n"
53+
BLOCK+="${INDENT}if os.getenv(\"NO_PROXY\"): self.no_proxy = os.getenv(\"NO_PROXY\")\n"
54+
BLOCK+="${INDENT}if os.getenv(\"no_proxy\"): self.no_proxy = os.getenv(\"no_proxy\")"
55+
56+
sed -i "${PROXY_LINE}a $BLOCK" "$CONFIG_FILE"
57+
echo "Inserted full proxy + no_proxy block after 'self.proxy = None'."
58+
else
59+
# self.no_proxy = None exists → insert only env logic after that
60+
BLOCK+="${INDENT}# Load proxy from environment variables (if set)\n"
61+
BLOCK+="${INDENT}if os.getenv(\"HTTPS_PROXY\"): self.proxy = os.getenv(\"HTTPS_PROXY\")\n"
62+
BLOCK+="${INDENT}if os.getenv(\"https_proxy\"): self.proxy = os.getenv(\"https_proxy\")\n"
63+
BLOCK+="${INDENT}if os.getenv(\"HTTP_PROXY\"): self.proxy = os.getenv(\"HTTP_PROXY\")\n"
64+
BLOCK+="${INDENT}if os.getenv(\"http_proxy\"): self.proxy = os.getenv(\"http_proxy\")\n"
65+
BLOCK+="${INDENT}# Load no_proxy from environment variables (if set)\n"
66+
BLOCK+="${INDENT}if os.getenv(\"NO_PROXY\"): self.no_proxy = os.getenv(\"NO_PROXY\")\n"
67+
BLOCK+="${INDENT}if os.getenv(\"no_proxy\"): self.no_proxy = os.getenv(\"no_proxy\")"
68+
69+
sed -i "${NO_PROXY_LINE}a $BLOCK" "$CONFIG_FILE"
70+
echo "Inserted environment block after 'self.no_proxy = None'."
71+
fi
72+
else
73+
echo "Warning: 'self.proxy = None' not found in $CONFIG_FILE. No proxy code inserted."
74+
fi
75+
else
76+
echo "Proxy environment code already present; no changes made."
77+
fi

scripts/release.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ git diff-index --quiet --cached HEAD || git commit -am "update changelog"
207207

208208
# Re-generate the client
209209
scripts/update-client.sh
210-
210+
#edit comfiguration.py files
211+
scripts/insert_proxy_config.sh
211212
# Apply hotfixes
212213
rm -r kubernetes/test/
213214
git add .

0 commit comments

Comments
 (0)