Merge pull request #2390 from p172913/master

Changes made in configuration.py to accept environmental variables
This commit is contained in:
Kubernetes Prow Robot 2025-06-24 13:00:31 -07:00 committed by GitHub
commit 51f4db5706
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 204 additions and 21 deletions

View File

@ -17,19 +17,70 @@ import unittest
from .ws_client import get_websocket_url
from .ws_client import websocket_proxycare
from kubernetes.client.configuration import Configuration
import os
import socket
import threading
import pytest
from kubernetes import stream, client, config
try:
import urllib3
urllib3.disable_warnings()
except ImportError:
pass
@pytest.fixture(autouse=True)
def dummy_kubeconfig(tmp_path, monkeypatch):
# Creating a kubeconfig
content = """
apiVersion: v1
kind: Config
clusters:
- name: default
cluster:
server: http://127.0.0.1:8888
contexts:
- name: default
context:
cluster: default
user: default
users:
- name: default
user: {}
current-context: default
"""
cfg_file = tmp_path / "kubeconfig"
cfg_file.write_text(content)
monkeypatch.setenv("KUBECONFIG", str(cfg_file))
def dictval(dict, key, default=None):
try:
val = dict[key]
except KeyError:
val = default
return val
def dictval(dict_obj, key, default=None):
return dict_obj.get(key, default)
class DummyProxy(threading.Thread):
"""
A minimal HTTP proxy that flags any CONNECT request and returns 200 OK.
Listens on 127.0.0.1:8888 by default.
"""
def __init__(self, host='127.0.0.1', port=8888):
super().__init__(daemon=True)
self.host = host
self.port = port
self.received_connect = False
self._server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._server_sock.bind((self.host, self.port))
self._server_sock.listen(1)
def run(self):
conn, _ = self._server_sock.accept()
try:
data = conn.recv(1024).decode('utf-8', errors='ignore')
if data.startswith('CONNECT '):
self.received_connect = True
conn.sendall(b"HTTP/1.1 200 Connection established\r\n\r\n")
finally:
conn.close()
class WSClientTest(unittest.TestCase):
@ -56,21 +107,68 @@ class WSClientTest(unittest.TestCase):
( 'http://proxy.example.com:8080/', 'user:pass', '.example.com', 'proxy.example.com', 8080, ('user','pass'), ['.example.com']),
( 'http://proxy.example.com:8080/', 'user:pass', 'localhost,.local,.example.com', 'proxy.example.com', 8080, ('user','pass'), ['localhost','.local','.example.com']),
]:
# setup input
config = Configuration()
if proxy is not None:
setattr(config, 'proxy', proxy)
if idpass is not None:
setattr(config, 'proxy_headers', urllib3.util.make_headers(proxy_basic_auth=idpass))
# input setup
cfg = Configuration()
if proxy:
cfg.proxy = proxy
if idpass:
cfg.proxy_headers = urllib3.util.make_headers(proxy_basic_auth=idpass)
if no_proxy is not None:
setattr(config, 'no_proxy', no_proxy)
# setup done
# test starts
connect_opt = websocket_proxycare( {}, config, None, None)
self.assertEqual( dictval(connect_opt,'http_proxy_host'), expect_host)
self.assertEqual( dictval(connect_opt,'http_proxy_port'), expect_port)
self.assertEqual( dictval(connect_opt,'http_proxy_auth'), expect_auth)
self.assertEqual( dictval(connect_opt,'http_no_proxy'), expect_noproxy)
cfg.no_proxy = no_proxy
connect_opts = websocket_proxycare({}, cfg, None, None)
assert dictval(connect_opts, 'http_proxy_host') == expect_host
assert dictval(connect_opts, 'http_proxy_port') == expect_port
assert dictval(connect_opts, 'http_proxy_auth') == expect_auth
assert dictval(connect_opts, 'http_no_proxy') == expect_noproxy
@pytest.fixture(scope="module")
def dummy_proxy():
#Dummy Proxy
proxy = DummyProxy(port=8888)
proxy.start()
yield proxy
@pytest.fixture(autouse=True)
def clear_proxy_env(monkeypatch):
for var in ("HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy", "NO_PROXY", "no_proxy"):
monkeypatch.delenv(var, raising=False)
def apply_proxy_to_conf():
#apply HTTPS_PROXY env var and set it as global.
cfg = client.Configuration.get_default_copy()
cfg.proxy = os.getenv("HTTPS_PROXY")
cfg.no_proxy = os.getenv("NO_PROXY", "")
client.Configuration.set_default(cfg)
def test_rest_call_ignores_env(dummy_proxy, monkeypatch):
# HTTPS_PROXY to dummy proxy
monkeypatch.setenv("HTTPS_PROXY", "http://127.0.0.1:8888")
# Avoid real HTTP request
monkeypatch.setattr(client.CoreV1Api, "list_namespace", lambda self, *_args, **_kwargs: None)
# Load config using kubeconfig
config.load_kube_config(config_file=os.environ["KUBECONFIG"])
apply_proxy_to_conf()
# HTTPS_PROXY to dummy proxy
monkeypatch.setenv("HTTPS_PROXY", "http://127.0.0.1:8888")
config.load_kube_config(config_file=os.environ["KUBECONFIG"])
apply_proxy_to_conf()
v1 = client.CoreV1Api()
v1.list_namespace(_preload_content=False)
assert not dummy_proxy.received_connect, "REST path should ignore HTTPS_PROXY"
def test_websocket_call_honors_env(dummy_proxy, monkeypatch):
# set HTTPS_PROXY again
monkeypatch.setenv("HTTPS_PROXY", "http://127.0.0.1:8888")
# Load kubeconfig
config.load_kube_config(config_file=os.environ["KUBECONFIG"])
apply_proxy_to_conf()
opts = websocket_proxycare({}, client.Configuration.get_default_copy(), None, None)
assert opts.get('http_proxy_host') == '127.0.0.1'
assert opts.get('http_proxy_port') == 8888
# Optionally verify no_proxy parsing
assert opts.get('http_no_proxy') is None
if __name__ == '__main__':
unittest.main()

View File

@ -17,6 +17,7 @@ import logging
import multiprocessing
import sys
import urllib3
import os
import six
from six.moves import http_client as httplib
@ -158,9 +159,15 @@ class Configuration(object):
"""
self.proxy = None
if(os.getenv("HTTPS_PROXY")):self.proxy=os.getenv("HTTPS_PROXY")
if(os.getenv("https_proxy")):self.proxy=os.getenv("https_proxy")
if(os.getenv("HTTP_PROXY")):self.proxy=os.getenv("HTTP_PROXY")
if(os.getenv("http_proxy")):self.proxy=os.getenv("http_proxy")
"""Proxy URL
"""
self.no_proxy = None
if(os.getenv("NO_PROXY")):self.no_proxy=os.getenv("NO_PROXY")
if(os.getenv("no_proxy")):self.no_proxy=os.getenv("no_proxy")
"""bypass proxy for host in the no_proxy list.
"""
self.proxy_headers = None

View File

@ -0,0 +1,77 @@
#!/usr/bin/env bash
# insert_proxy_config.sh - run this after openapi-generator release.sh
CONFIG_PATH="../python_kubernetes/kubernetes/client"
# Compute the full file path
CONFIG_FILE="$CONFIG_PATH/configuration.py"
# --- Normalize Windows-style backslashes to Unix forward slashes ---
CONFIG_FILE="$(echo "$CONFIG_FILE" | sed 's|\\|/|g')"
# --- Ensure the target file exists and is writable ---
if [ ! -f "$CONFIG_FILE" ] || [ ! -w "$CONFIG_FILE" ]; then
echo "Error: $CONFIG_FILE does not exist or is not writable." >&2
exit 1
fi
# --- Step 1: Ensure 'import os' follows existing imports (idempotent) ---
if ! grep -qE '^import os$' "$CONFIG_FILE"; then
LAST_IMPORT=$(grep -nE '^(import |from )' "$CONFIG_FILE" | tail -n1 | cut -d: -f1)
if [ -n "$LAST_IMPORT" ]; then
sed -i "$((LAST_IMPORT+1))i import os" "$CONFIG_FILE"
else
if head -n1 "$CONFIG_FILE" | grep -q '^#!'; then
sed -i '2i import os' "$CONFIG_FILE"
else
sed -i '1i import os' "$CONFIG_FILE"
fi
fi
echo "Inserted 'import os' after existing imports in $CONFIG_FILE."
else
echo "'import os' already present; no changes made."
fi
# --- Step 2: Insert proxy & no_proxy environment code ---
if ! grep -q 'os.getenv("HTTPS_PROXY"' "$CONFIG_FILE"; then
PROXY_LINE=$(grep -n "self.proxy = None" "$CONFIG_FILE" | cut -d: -f1)
NO_PROXY_LINE=$(grep -n "^[[:space:]]*self\.no_proxy[[:space:]]*=[[:space:]]*None" "$CONFIG_FILE" | cut -d: -f1)
if [ -n "$PROXY_LINE" ]; then
INDENT=$(sed -n "${PROXY_LINE}s/^\( *\).*/\1/p" "$CONFIG_FILE")
BLOCK=""
if [ -z "$NO_PROXY_LINE" ]; then
# self.no_proxy = None is not present → insert full block after self.proxy = None
BLOCK+="${INDENT}# Load proxy from environment variables (if set)\n"
BLOCK+="${INDENT}if os.getenv(\"HTTPS_PROXY\"): self.proxy = os.getenv(\"HTTPS_PROXY\")\n"
BLOCK+="${INDENT}if os.getenv(\"https_proxy\"): self.proxy = os.getenv(\"https_proxy\")\n"
BLOCK+="${INDENT}if os.getenv(\"HTTP_PROXY\"): self.proxy = os.getenv(\"HTTP_PROXY\")\n"
BLOCK+="${INDENT}if os.getenv(\"http_proxy\"): self.proxy = os.getenv(\"http_proxy\")\n"
BLOCK+="${INDENT}self.no_proxy = None\n"
BLOCK+="${INDENT}# Load no_proxy from environment variables (if set)\n"
BLOCK+="${INDENT}if os.getenv(\"NO_PROXY\"): self.no_proxy = os.getenv(\"NO_PROXY\")\n"
BLOCK+="${INDENT}if os.getenv(\"no_proxy\"): self.no_proxy = os.getenv(\"no_proxy\")"
sed -i "${PROXY_LINE}a $BLOCK" "$CONFIG_FILE"
echo "Inserted full proxy + no_proxy block after 'self.proxy = None'."
else
# self.no_proxy = None exists → insert only env logic after that
BLOCK+="${INDENT}# Load proxy from environment variables (if set)\n"
BLOCK+="${INDENT}if os.getenv(\"HTTPS_PROXY\"): self.proxy = os.getenv(\"HTTPS_PROXY\")\n"
BLOCK+="${INDENT}if os.getenv(\"https_proxy\"): self.proxy = os.getenv(\"https_proxy\")\n"
BLOCK+="${INDENT}if os.getenv(\"HTTP_PROXY\"): self.proxy = os.getenv(\"HTTP_PROXY\")\n"
BLOCK+="${INDENT}if os.getenv(\"http_proxy\"): self.proxy = os.getenv(\"http_proxy\")\n"
BLOCK+="${INDENT}# Load no_proxy from environment variables (if set)\n"
BLOCK+="${INDENT}if os.getenv(\"NO_PROXY\"): self.no_proxy = os.getenv(\"NO_PROXY\")\n"
BLOCK+="${INDENT}if os.getenv(\"no_proxy\"): self.no_proxy = os.getenv(\"no_proxy\")"
sed -i "${NO_PROXY_LINE}a $BLOCK" "$CONFIG_FILE"
echo "Inserted environment block after 'self.no_proxy = None'."
fi
else
echo "Warning: 'self.proxy = None' not found in $CONFIG_FILE. No proxy code inserted."
fi
else
echo "Proxy environment code already present; no changes made."
fi

View File

@ -207,7 +207,8 @@ git diff-index --quiet --cached HEAD || git commit -am "update changelog"
# Re-generate the client
scripts/update-client.sh
#edit comfiguration.py files
scripts/insert_proxy_config.sh
# Apply hotfixes
rm -r kubernetes/test/
git add .