mirror of
https://github.com/ansible-collections/community.docker.git
synced 2026-07-29 03:46:55 +00:00
Add typing information, 1/2 (#1176)
* Re-enable typing and improve config. * Make mypy pass. * Improve settings. * First batch of types. * Add more type hints. * Fixes. * Format. * Fix split_port() without returning to previous type chaos. * Continue with type hints (and ignores).
This commit is contained in:
@@ -15,7 +15,7 @@ from .._import_helper import HTTPAdapter as _HTTPAdapter
|
||||
|
||||
|
||||
class BaseHTTPAdapter(_HTTPAdapter):
|
||||
def close(self):
|
||||
def close(self) -> None:
|
||||
super().close()
|
||||
if hasattr(self, "pools"):
|
||||
self.pools.clear()
|
||||
@@ -24,10 +24,10 @@ class BaseHTTPAdapter(_HTTPAdapter):
|
||||
# https://github.com/psf/requests/commit/c0813a2d910ea6b4f8438b91d315b8d181302356
|
||||
# changes requests.adapters.HTTPAdapter to no longer call get_connection() from
|
||||
# send(), but instead call _get_connection().
|
||||
def _get_connection(self, request, *args, **kwargs):
|
||||
def _get_connection(self, request, *args, **kwargs): # type: ignore
|
||||
return self.get_connection(request.url, kwargs.get("proxies"))
|
||||
|
||||
# Fix for requests 2.32.2+:
|
||||
# https://github.com/psf/requests/commit/c98e4d133ef29c46a9b68cd783087218a8075e05
|
||||
def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
|
||||
def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): # type: ignore
|
||||
return self.get_connection(request.url, proxies)
|
||||
|
||||
@@ -23,12 +23,12 @@ RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer
|
||||
|
||||
|
||||
class NpipeHTTPConnection(urllib3_connection.HTTPConnection):
|
||||
def __init__(self, npipe_path, timeout=60):
|
||||
def __init__(self, npipe_path: str, timeout: int | float = 60) -> None:
|
||||
super().__init__("localhost", timeout=timeout)
|
||||
self.npipe_path = npipe_path
|
||||
self.timeout = timeout
|
||||
|
||||
def connect(self):
|
||||
def connect(self) -> None:
|
||||
sock = NpipeSocket()
|
||||
sock.settimeout(self.timeout)
|
||||
sock.connect(self.npipe_path)
|
||||
@@ -36,18 +36,20 @@ class NpipeHTTPConnection(urllib3_connection.HTTPConnection):
|
||||
|
||||
|
||||
class NpipeHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool):
|
||||
def __init__(self, npipe_path, timeout=60, maxsize=10):
|
||||
def __init__(
|
||||
self, npipe_path: str, timeout: int | float = 60, maxsize: int = 10
|
||||
) -> None:
|
||||
super().__init__("localhost", timeout=timeout, maxsize=maxsize)
|
||||
self.npipe_path = npipe_path
|
||||
self.timeout = timeout
|
||||
|
||||
def _new_conn(self):
|
||||
def _new_conn(self) -> NpipeHTTPConnection:
|
||||
return NpipeHTTPConnection(self.npipe_path, self.timeout)
|
||||
|
||||
# When re-using connections, urllib3 tries to call select() on our
|
||||
# NpipeSocket instance, causing a crash. To circumvent this, we override
|
||||
# _get_conn, where that check happens.
|
||||
def _get_conn(self, timeout):
|
||||
def _get_conn(self, timeout: int | float) -> NpipeHTTPConnection:
|
||||
conn = None
|
||||
try:
|
||||
conn = self.pool.get(block=self.block, timeout=timeout)
|
||||
@@ -67,7 +69,6 @@ class NpipeHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool):
|
||||
|
||||
|
||||
class NpipeHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
__attrs__ = HTTPAdapter.__attrs__ + [
|
||||
"npipe_path",
|
||||
"pools",
|
||||
@@ -77,11 +78,11 @@ class NpipeHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_url,
|
||||
timeout=60,
|
||||
pool_connections=constants.DEFAULT_NUM_POOLS,
|
||||
max_pool_size=constants.DEFAULT_MAX_POOL_SIZE,
|
||||
):
|
||||
base_url: str,
|
||||
timeout: int | float = 60,
|
||||
pool_connections: int = constants.DEFAULT_NUM_POOLS,
|
||||
max_pool_size: int = constants.DEFAULT_MAX_POOL_SIZE,
|
||||
) -> None:
|
||||
self.npipe_path = base_url.replace("npipe://", "")
|
||||
self.timeout = timeout
|
||||
self.max_pool_size = max_pool_size
|
||||
@@ -90,7 +91,7 @@ class NpipeHTTPAdapter(BaseHTTPAdapter):
|
||||
)
|
||||
super().__init__()
|
||||
|
||||
def get_connection(self, url, proxies=None):
|
||||
def get_connection(self, url: str | bytes, proxies=None) -> NpipeHTTPConnectionPool:
|
||||
with self.pools.lock:
|
||||
pool = self.pools.get(url)
|
||||
if pool:
|
||||
@@ -103,7 +104,7 @@ class NpipeHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
return pool
|
||||
|
||||
def request_url(self, request, proxies):
|
||||
def request_url(self, request, proxies) -> str:
|
||||
# The select_proxy utility in requests errors out when the provided URL
|
||||
# does not have a hostname, like is the case when using a UNIX socket.
|
||||
# Since proxies are an irrelevant notion in the case of UNIX sockets
|
||||
|
||||
@@ -15,8 +15,10 @@ import functools
|
||||
import io
|
||||
import time
|
||||
import traceback
|
||||
import typing as t
|
||||
|
||||
|
||||
PYWIN32_IMPORT_ERROR: str | None # pylint: disable=invalid-name
|
||||
try:
|
||||
import pywintypes
|
||||
import win32api
|
||||
@@ -28,6 +30,13 @@ except ImportError:
|
||||
else:
|
||||
PYWIN32_IMPORT_ERROR = None # pylint: disable=invalid-name
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from collections.abc import Buffer, Callable
|
||||
|
||||
_Self = t.TypeVar("_Self")
|
||||
_P = t.ParamSpec("_P")
|
||||
_R = t.TypeVar("_R")
|
||||
|
||||
|
||||
ERROR_PIPE_BUSY = 0xE7
|
||||
SECURITY_SQOS_PRESENT = 0x100000
|
||||
@@ -36,10 +45,12 @@ SECURITY_ANONYMOUS = 0
|
||||
MAXIMUM_RETRY_COUNT = 10
|
||||
|
||||
|
||||
def check_closed(f):
|
||||
def check_closed(
|
||||
f: Callable[t.Concatenate[_Self, _P], _R],
|
||||
) -> Callable[t.Concatenate[_Self, _P], _R]:
|
||||
@functools.wraps(f)
|
||||
def wrapped(self, *args, **kwargs):
|
||||
if self._closed:
|
||||
def wrapped(self: _Self, *args: _P.args, **kwargs: _P.kwargs) -> _R:
|
||||
if self._closed: # type: ignore
|
||||
raise RuntimeError("Can not reuse socket after connection was closed.")
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
@@ -53,25 +64,25 @@ class NpipeSocket:
|
||||
implemented.
|
||||
"""
|
||||
|
||||
def __init__(self, handle=None):
|
||||
def __init__(self, handle=None) -> None:
|
||||
self._timeout = win32pipe.NMPWAIT_USE_DEFAULT_WAIT
|
||||
self._handle = handle
|
||||
self._address = None
|
||||
self._address: str | None = None
|
||||
self._closed = False
|
||||
self.flags = None
|
||||
self.flags: int | None = None
|
||||
|
||||
def accept(self):
|
||||
def accept(self) -> t.NoReturn:
|
||||
raise NotImplementedError()
|
||||
|
||||
def bind(self, address):
|
||||
def bind(self, address) -> t.NoReturn:
|
||||
raise NotImplementedError()
|
||||
|
||||
def close(self):
|
||||
def close(self) -> None:
|
||||
self._handle.Close()
|
||||
self._closed = True
|
||||
|
||||
@check_closed
|
||||
def connect(self, address, retry_count=0):
|
||||
def connect(self, address, retry_count: int = 0) -> None:
|
||||
try:
|
||||
handle = win32file.CreateFile(
|
||||
address,
|
||||
@@ -99,14 +110,14 @@ class NpipeSocket:
|
||||
return self.connect(address, retry_count)
|
||||
raise e
|
||||
|
||||
self.flags = win32pipe.GetNamedPipeInfo(handle)[0]
|
||||
self.flags = win32pipe.GetNamedPipeInfo(handle)[0] # type: ignore
|
||||
|
||||
self._handle = handle
|
||||
self._address = address
|
||||
|
||||
@check_closed
|
||||
def connect_ex(self, address):
|
||||
return self.connect(address)
|
||||
def connect_ex(self, address) -> None:
|
||||
self.connect(address)
|
||||
|
||||
@check_closed
|
||||
def detach(self):
|
||||
@@ -114,25 +125,25 @@ class NpipeSocket:
|
||||
return self._handle
|
||||
|
||||
@check_closed
|
||||
def dup(self):
|
||||
def dup(self) -> NpipeSocket:
|
||||
return NpipeSocket(self._handle)
|
||||
|
||||
def getpeername(self):
|
||||
def getpeername(self) -> str | None:
|
||||
return self._address
|
||||
|
||||
def getsockname(self):
|
||||
def getsockname(self) -> str | None:
|
||||
return self._address
|
||||
|
||||
def getsockopt(self, level, optname, buflen=None):
|
||||
def getsockopt(self, level, optname, buflen=None) -> t.NoReturn:
|
||||
raise NotImplementedError()
|
||||
|
||||
def ioctl(self, control, option):
|
||||
def ioctl(self, control, option) -> t.NoReturn:
|
||||
raise NotImplementedError()
|
||||
|
||||
def listen(self, backlog):
|
||||
def listen(self, backlog) -> t.NoReturn:
|
||||
raise NotImplementedError()
|
||||
|
||||
def makefile(self, mode=None, bufsize=None):
|
||||
def makefile(self, mode: str, bufsize: int | None = None):
|
||||
if mode.strip("b") != "r":
|
||||
raise NotImplementedError()
|
||||
rawio = NpipeFileIOBase(self)
|
||||
@@ -141,30 +152,30 @@ class NpipeSocket:
|
||||
return io.BufferedReader(rawio, buffer_size=bufsize)
|
||||
|
||||
@check_closed
|
||||
def recv(self, bufsize, flags=0):
|
||||
def recv(self, bufsize: int, flags: int = 0) -> str:
|
||||
dummy_err, data = win32file.ReadFile(self._handle, bufsize)
|
||||
return data
|
||||
|
||||
@check_closed
|
||||
def recvfrom(self, bufsize, flags=0):
|
||||
def recvfrom(self, bufsize: int, flags: int = 0) -> tuple[str, str | None]:
|
||||
data = self.recv(bufsize, flags)
|
||||
return (data, self._address)
|
||||
|
||||
@check_closed
|
||||
def recvfrom_into(self, buf, nbytes=0, flags=0):
|
||||
return self.recv_into(buf, nbytes, flags), self._address
|
||||
def recvfrom_into(
|
||||
self, buf: Buffer, nbytes: int = 0, flags: int = 0
|
||||
) -> tuple[int, str | None]:
|
||||
return self.recv_into(buf, nbytes), self._address
|
||||
|
||||
@check_closed
|
||||
def recv_into(self, buf, nbytes=0):
|
||||
readbuf = buf
|
||||
if not isinstance(buf, memoryview):
|
||||
readbuf = memoryview(buf)
|
||||
def recv_into(self, buf: Buffer, nbytes: int = 0) -> int:
|
||||
readbuf = buf if isinstance(buf, memoryview) else memoryview(buf)
|
||||
|
||||
event = win32event.CreateEvent(None, True, True, None)
|
||||
try:
|
||||
overlapped = pywintypes.OVERLAPPED()
|
||||
overlapped.hEvent = event
|
||||
dummy_err, dummy_data = win32file.ReadFile(
|
||||
dummy_err, dummy_data = win32file.ReadFile( # type: ignore
|
||||
self._handle, readbuf[:nbytes] if nbytes else readbuf, overlapped
|
||||
)
|
||||
wait_result = win32event.WaitForSingleObject(event, self._timeout)
|
||||
@@ -176,12 +187,12 @@ class NpipeSocket:
|
||||
win32api.CloseHandle(event)
|
||||
|
||||
@check_closed
|
||||
def send(self, string, flags=0):
|
||||
def send(self, string: Buffer, flags: int = 0) -> int:
|
||||
event = win32event.CreateEvent(None, True, True, None)
|
||||
try:
|
||||
overlapped = pywintypes.OVERLAPPED()
|
||||
overlapped.hEvent = event
|
||||
win32file.WriteFile(self._handle, string, overlapped)
|
||||
win32file.WriteFile(self._handle, string, overlapped) # type: ignore
|
||||
wait_result = win32event.WaitForSingleObject(event, self._timeout)
|
||||
if wait_result == win32event.WAIT_TIMEOUT:
|
||||
win32file.CancelIo(self._handle)
|
||||
@@ -191,20 +202,20 @@ class NpipeSocket:
|
||||
win32api.CloseHandle(event)
|
||||
|
||||
@check_closed
|
||||
def sendall(self, string, flags=0):
|
||||
def sendall(self, string: Buffer, flags: int = 0) -> int:
|
||||
return self.send(string, flags)
|
||||
|
||||
@check_closed
|
||||
def sendto(self, string, address):
|
||||
def sendto(self, string: Buffer, address: str) -> int:
|
||||
self.connect(address)
|
||||
return self.send(string)
|
||||
|
||||
def setblocking(self, flag):
|
||||
def setblocking(self, flag: bool):
|
||||
if flag:
|
||||
return self.settimeout(None)
|
||||
return self.settimeout(0)
|
||||
|
||||
def settimeout(self, value):
|
||||
def settimeout(self, value: int | float | None) -> None:
|
||||
if value is None:
|
||||
# Blocking mode
|
||||
self._timeout = win32event.INFINITE
|
||||
@@ -214,39 +225,39 @@ class NpipeSocket:
|
||||
# Timeout mode - Value converted to milliseconds
|
||||
self._timeout = int(value * 1000)
|
||||
|
||||
def gettimeout(self):
|
||||
def gettimeout(self) -> int | float | None:
|
||||
return self._timeout
|
||||
|
||||
def setsockopt(self, level, optname, value):
|
||||
def setsockopt(self, level, optname, value) -> t.NoReturn:
|
||||
raise NotImplementedError()
|
||||
|
||||
@check_closed
|
||||
def shutdown(self, how):
|
||||
def shutdown(self, how) -> None:
|
||||
return self.close()
|
||||
|
||||
|
||||
class NpipeFileIOBase(io.RawIOBase):
|
||||
def __init__(self, npipe_socket):
|
||||
def __init__(self, npipe_socket) -> None:
|
||||
self.sock = npipe_socket
|
||||
|
||||
def close(self):
|
||||
def close(self) -> None:
|
||||
super().close()
|
||||
self.sock = None
|
||||
|
||||
def fileno(self):
|
||||
def fileno(self) -> int:
|
||||
return self.sock.fileno()
|
||||
|
||||
def isatty(self):
|
||||
def isatty(self) -> bool:
|
||||
return False
|
||||
|
||||
def readable(self):
|
||||
def readable(self) -> bool:
|
||||
return True
|
||||
|
||||
def readinto(self, buf):
|
||||
def readinto(self, buf: Buffer) -> int:
|
||||
return self.sock.recv_into(buf)
|
||||
|
||||
def seekable(self):
|
||||
def seekable(self) -> bool:
|
||||
return False
|
||||
|
||||
def writable(self):
|
||||
def writable(self) -> bool:
|
||||
return False
|
||||
|
||||
@@ -17,6 +17,7 @@ import signal
|
||||
import socket
|
||||
import subprocess
|
||||
import traceback
|
||||
import typing as t
|
||||
from queue import Empty
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@@ -25,6 +26,7 @@ from .._import_helper import HTTPAdapter, urllib3, urllib3_connection
|
||||
from .basehttpadapter import BaseHTTPAdapter
|
||||
|
||||
|
||||
PARAMIKO_IMPORT_ERROR: str | None # pylint: disable=invalid-name
|
||||
try:
|
||||
import paramiko
|
||||
except ImportError:
|
||||
@@ -32,12 +34,15 @@ except ImportError:
|
||||
else:
|
||||
PARAMIKO_IMPORT_ERROR = None # pylint: disable=invalid-name
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from collections.abc import Buffer
|
||||
|
||||
|
||||
RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer
|
||||
|
||||
|
||||
class SSHSocket(socket.socket):
|
||||
def __init__(self, host):
|
||||
def __init__(self, host: str) -> None:
|
||||
super().__init__(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.host = host
|
||||
self.port = None
|
||||
@@ -47,9 +52,9 @@ class SSHSocket(socket.socket):
|
||||
if "@" in self.host:
|
||||
self.user, self.host = self.host.split("@")
|
||||
|
||||
self.proc = None
|
||||
self.proc: subprocess.Popen | None = None
|
||||
|
||||
def connect(self, **kwargs):
|
||||
def connect(self, *args_: t.Any, **kwargs: t.Any) -> None:
|
||||
args = ["ssh"]
|
||||
if self.user:
|
||||
args = args + ["-l", self.user]
|
||||
@@ -81,37 +86,48 @@ class SSHSocket(socket.socket):
|
||||
preexec_fn=preexec_func,
|
||||
)
|
||||
|
||||
def _write(self, data):
|
||||
if not self.proc or self.proc.stdin.closed:
|
||||
def _write(self, data: Buffer) -> int:
|
||||
if not self.proc:
|
||||
raise RuntimeError(
|
||||
"SSH subprocess not initiated. connect() must be called first."
|
||||
)
|
||||
assert self.proc.stdin is not None
|
||||
if self.proc.stdin.closed:
|
||||
raise RuntimeError(
|
||||
"SSH subprocess not initiated. connect() must be called first after close()."
|
||||
)
|
||||
written = self.proc.stdin.write(data)
|
||||
self.proc.stdin.flush()
|
||||
return written
|
||||
|
||||
def sendall(self, data):
|
||||
def sendall(self, data: Buffer, *args, **kwargs) -> None:
|
||||
self._write(data)
|
||||
|
||||
def send(self, data):
|
||||
def send(self, data: Buffer, *args, **kwargs) -> int:
|
||||
return self._write(data)
|
||||
|
||||
def recv(self, n):
|
||||
def recv(self, n: int, *args, **kwargs) -> bytes:
|
||||
if not self.proc:
|
||||
raise RuntimeError(
|
||||
"SSH subprocess not initiated. connect() must be called first."
|
||||
)
|
||||
assert self.proc.stdout is not None
|
||||
return self.proc.stdout.read(n)
|
||||
|
||||
def makefile(self, mode):
|
||||
def makefile(self, mode: str, *args, **kwargs) -> t.IO: # type: ignore
|
||||
if not self.proc:
|
||||
self.connect()
|
||||
self.proc.stdout.channel = self
|
||||
assert self.proc is not None
|
||||
assert self.proc.stdout is not None
|
||||
self.proc.stdout.channel = self # type: ignore
|
||||
|
||||
return self.proc.stdout
|
||||
|
||||
def close(self):
|
||||
if not self.proc or self.proc.stdin.closed:
|
||||
def close(self) -> None:
|
||||
if not self.proc:
|
||||
return
|
||||
assert self.proc.stdin is not None
|
||||
if self.proc.stdin.closed:
|
||||
return
|
||||
self.proc.stdin.write(b"\n\n")
|
||||
self.proc.stdin.flush()
|
||||
@@ -119,13 +135,19 @@ class SSHSocket(socket.socket):
|
||||
|
||||
|
||||
class SSHConnection(urllib3_connection.HTTPConnection):
|
||||
def __init__(self, ssh_transport=None, timeout=60, host=None):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
ssh_transport=None,
|
||||
timeout: int | float = 60,
|
||||
host: str,
|
||||
) -> None:
|
||||
super().__init__("localhost", timeout=timeout)
|
||||
self.ssh_transport = ssh_transport
|
||||
self.timeout = timeout
|
||||
self.ssh_host = host
|
||||
|
||||
def connect(self):
|
||||
def connect(self) -> None:
|
||||
if self.ssh_transport:
|
||||
sock = self.ssh_transport.open_session()
|
||||
sock.settimeout(self.timeout)
|
||||
@@ -141,7 +163,14 @@ class SSHConnection(urllib3_connection.HTTPConnection):
|
||||
class SSHConnectionPool(urllib3.connectionpool.HTTPConnectionPool):
|
||||
scheme = "ssh"
|
||||
|
||||
def __init__(self, ssh_client=None, timeout=60, maxsize=10, host=None):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
ssh_client: paramiko.SSHClient | None = None,
|
||||
timeout: int | float = 60,
|
||||
maxsize: int = 10,
|
||||
host: str,
|
||||
) -> None:
|
||||
super().__init__("localhost", timeout=timeout, maxsize=maxsize)
|
||||
self.ssh_transport = None
|
||||
self.timeout = timeout
|
||||
@@ -149,13 +178,17 @@ class SSHConnectionPool(urllib3.connectionpool.HTTPConnectionPool):
|
||||
self.ssh_transport = ssh_client.get_transport()
|
||||
self.ssh_host = host
|
||||
|
||||
def _new_conn(self):
|
||||
return SSHConnection(self.ssh_transport, self.timeout, self.ssh_host)
|
||||
def _new_conn(self) -> SSHConnection:
|
||||
return SSHConnection(
|
||||
ssh_transport=self.ssh_transport,
|
||||
timeout=self.timeout,
|
||||
host=self.ssh_host,
|
||||
)
|
||||
|
||||
# When re-using connections, urllib3 calls fileno() on our
|
||||
# SSH channel instance, quickly overloading our fd limit. To avoid this,
|
||||
# we override _get_conn
|
||||
def _get_conn(self, timeout):
|
||||
def _get_conn(self, timeout: int | float) -> SSHConnection:
|
||||
conn = None
|
||||
try:
|
||||
conn = self.pool.get(block=self.block, timeout=timeout)
|
||||
@@ -175,7 +208,6 @@ class SSHConnectionPool(urllib3.connectionpool.HTTPConnectionPool):
|
||||
|
||||
|
||||
class SSHHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
__attrs__ = HTTPAdapter.__attrs__ + [
|
||||
"pools",
|
||||
"timeout",
|
||||
@@ -186,13 +218,13 @@ class SSHHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_url,
|
||||
timeout=60,
|
||||
pool_connections=constants.DEFAULT_NUM_POOLS,
|
||||
max_pool_size=constants.DEFAULT_MAX_POOL_SIZE,
|
||||
shell_out=False,
|
||||
):
|
||||
self.ssh_client = None
|
||||
base_url: str,
|
||||
timeout: int | float = 60,
|
||||
pool_connections: int = constants.DEFAULT_NUM_POOLS,
|
||||
max_pool_size: int = constants.DEFAULT_MAX_POOL_SIZE,
|
||||
shell_out: bool = False,
|
||||
) -> None:
|
||||
self.ssh_client: paramiko.SSHClient | None = None
|
||||
if not shell_out:
|
||||
self._create_paramiko_client(base_url)
|
||||
self._connect()
|
||||
@@ -208,30 +240,31 @@ class SSHHTTPAdapter(BaseHTTPAdapter):
|
||||
)
|
||||
super().__init__()
|
||||
|
||||
def _create_paramiko_client(self, base_url):
|
||||
def _create_paramiko_client(self, base_url: str) -> None:
|
||||
logging.getLogger("paramiko").setLevel(logging.WARNING)
|
||||
self.ssh_client = paramiko.SSHClient()
|
||||
base_url = urlparse(base_url)
|
||||
self.ssh_params = {
|
||||
"hostname": base_url.hostname,
|
||||
"port": base_url.port,
|
||||
"username": base_url.username,
|
||||
base_url_p = urlparse(base_url)
|
||||
assert base_url_p.hostname is not None
|
||||
self.ssh_params: dict[str, t.Any] = {
|
||||
"hostname": base_url_p.hostname,
|
||||
"port": base_url_p.port,
|
||||
"username": base_url_p.username,
|
||||
}
|
||||
ssh_config_file = os.path.expanduser("~/.ssh/config")
|
||||
if os.path.exists(ssh_config_file):
|
||||
conf = paramiko.SSHConfig()
|
||||
with open(ssh_config_file, "rt", encoding="utf-8") as f:
|
||||
conf.parse(f)
|
||||
host_config = conf.lookup(base_url.hostname)
|
||||
host_config = conf.lookup(base_url_p.hostname)
|
||||
if "proxycommand" in host_config:
|
||||
self.ssh_params["sock"] = paramiko.ProxyCommand(
|
||||
host_config["proxycommand"]
|
||||
)
|
||||
if "hostname" in host_config:
|
||||
self.ssh_params["hostname"] = host_config["hostname"]
|
||||
if base_url.port is None and "port" in host_config:
|
||||
if base_url_p.port is None and "port" in host_config:
|
||||
self.ssh_params["port"] = host_config["port"]
|
||||
if base_url.username is None and "user" in host_config:
|
||||
if base_url_p.username is None and "user" in host_config:
|
||||
self.ssh_params["username"] = host_config["user"]
|
||||
if "identityfile" in host_config:
|
||||
self.ssh_params["key_filename"] = host_config["identityfile"]
|
||||
@@ -239,11 +272,11 @@ class SSHHTTPAdapter(BaseHTTPAdapter):
|
||||
self.ssh_client.load_system_host_keys()
|
||||
self.ssh_client.set_missing_host_key_policy(paramiko.RejectPolicy())
|
||||
|
||||
def _connect(self):
|
||||
def _connect(self) -> None:
|
||||
if self.ssh_client:
|
||||
self.ssh_client.connect(**self.ssh_params)
|
||||
|
||||
def get_connection(self, url, proxies=None):
|
||||
def get_connection(self, url: str | bytes, proxies=None) -> SSHConnectionPool:
|
||||
if not self.ssh_client:
|
||||
return SSHConnectionPool(
|
||||
ssh_client=self.ssh_client,
|
||||
@@ -270,7 +303,7 @@ class SSHHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
return pool
|
||||
|
||||
def close(self):
|
||||
def close(self) -> None:
|
||||
super().close()
|
||||
if self.ssh_client:
|
||||
self.ssh_client.close()
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils._version import (
|
||||
LooseVersion,
|
||||
)
|
||||
import typing as t
|
||||
|
||||
from .._import_helper import HTTPAdapter, urllib3
|
||||
from .basehttpadapter import BaseHTTPAdapter
|
||||
@@ -30,14 +28,19 @@ PoolManager = urllib3.poolmanager.PoolManager
|
||||
class SSLHTTPAdapter(BaseHTTPAdapter):
|
||||
"""An HTTPS Transport Adapter that uses an arbitrary SSL version."""
|
||||
|
||||
__attrs__ = HTTPAdapter.__attrs__ + ["assert_hostname", "ssl_version"]
|
||||
__attrs__ = HTTPAdapter.__attrs__ + ["assert_hostname"]
|
||||
|
||||
def __init__(self, ssl_version=None, assert_hostname=None, **kwargs):
|
||||
self.ssl_version = ssl_version
|
||||
def __init__(
|
||||
self,
|
||||
assert_hostname: bool | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.assert_hostname = assert_hostname
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def init_poolmanager(self, connections, maxsize, block=False):
|
||||
def init_poolmanager(
|
||||
self, connections: int, maxsize: int, block: bool = False, **kwargs: t.Any
|
||||
) -> None:
|
||||
kwargs = {
|
||||
"num_pools": connections,
|
||||
"maxsize": maxsize,
|
||||
@@ -45,12 +48,10 @@ class SSLHTTPAdapter(BaseHTTPAdapter):
|
||||
}
|
||||
if self.assert_hostname is not None:
|
||||
kwargs["assert_hostname"] = self.assert_hostname
|
||||
if self.ssl_version and self.can_override_ssl_version():
|
||||
kwargs["ssl_version"] = self.ssl_version
|
||||
|
||||
self.poolmanager = PoolManager(**kwargs)
|
||||
|
||||
def get_connection(self, *args, **kwargs):
|
||||
def get_connection(self, *args, **kwargs) -> urllib3.ConnectionPool:
|
||||
"""
|
||||
Ensure assert_hostname is set correctly on our pool
|
||||
|
||||
@@ -61,15 +62,7 @@ class SSLHTTPAdapter(BaseHTTPAdapter):
|
||||
conn = super().get_connection(*args, **kwargs)
|
||||
if (
|
||||
self.assert_hostname is not None
|
||||
and conn.assert_hostname != self.assert_hostname
|
||||
and conn.assert_hostname != self.assert_hostname # type: ignore
|
||||
):
|
||||
conn.assert_hostname = self.assert_hostname
|
||||
conn.assert_hostname = self.assert_hostname # type: ignore
|
||||
return conn
|
||||
|
||||
def can_override_ssl_version(self):
|
||||
urllib_ver = urllib3.__version__.split("-")[0]
|
||||
if urllib_ver is None:
|
||||
return False
|
||||
if urllib_ver == "dev":
|
||||
return True
|
||||
return LooseVersion(urllib_ver) > LooseVersion("1.5")
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import socket
|
||||
import typing as t
|
||||
|
||||
from .. import constants
|
||||
from .._import_helper import HTTPAdapter, urllib3, urllib3_connection
|
||||
@@ -22,26 +23,27 @@ RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer
|
||||
|
||||
|
||||
class UnixHTTPConnection(urllib3_connection.HTTPConnection):
|
||||
|
||||
def __init__(self, base_url, unix_socket, timeout=60):
|
||||
def __init__(
|
||||
self, base_url: str | bytes, unix_socket, timeout: int | float = 60
|
||||
) -> None:
|
||||
super().__init__("localhost", timeout=timeout)
|
||||
self.base_url = base_url
|
||||
self.unix_socket = unix_socket
|
||||
self.timeout = timeout
|
||||
self.disable_buffering = False
|
||||
|
||||
def connect(self):
|
||||
def connect(self) -> None:
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.settimeout(self.timeout)
|
||||
sock.connect(self.unix_socket)
|
||||
self.sock = sock
|
||||
|
||||
def putheader(self, header, *values):
|
||||
def putheader(self, header: str, *values: str) -> None:
|
||||
super().putheader(header, *values)
|
||||
if header == "Connection" and "Upgrade" in values:
|
||||
self.disable_buffering = True
|
||||
|
||||
def response_class(self, sock, *args, **kwargs):
|
||||
def response_class(self, sock, *args, **kwargs) -> t.Any:
|
||||
# FIXME: We may need to disable buffering on Py3,
|
||||
# but there's no clear way to do it at the moment. See:
|
||||
# https://github.com/docker/docker-py/issues/1799
|
||||
@@ -49,18 +51,23 @@ class UnixHTTPConnection(urllib3_connection.HTTPConnection):
|
||||
|
||||
|
||||
class UnixHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool):
|
||||
def __init__(self, base_url, socket_path, timeout=60, maxsize=10):
|
||||
def __init__(
|
||||
self,
|
||||
base_url: str | bytes,
|
||||
socket_path: str,
|
||||
timeout: int | float = 60,
|
||||
maxsize: int = 10,
|
||||
) -> None:
|
||||
super().__init__("localhost", timeout=timeout, maxsize=maxsize)
|
||||
self.base_url = base_url
|
||||
self.socket_path = socket_path
|
||||
self.timeout = timeout
|
||||
|
||||
def _new_conn(self):
|
||||
def _new_conn(self) -> UnixHTTPConnection:
|
||||
return UnixHTTPConnection(self.base_url, self.socket_path, self.timeout)
|
||||
|
||||
|
||||
class UnixHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
__attrs__ = HTTPAdapter.__attrs__ + [
|
||||
"pools",
|
||||
"socket_path",
|
||||
@@ -70,11 +77,11 @@ class UnixHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
socket_url,
|
||||
timeout=60,
|
||||
pool_connections=constants.DEFAULT_NUM_POOLS,
|
||||
max_pool_size=constants.DEFAULT_MAX_POOL_SIZE,
|
||||
):
|
||||
socket_url: str,
|
||||
timeout: int | float = 60,
|
||||
pool_connections: int = constants.DEFAULT_NUM_POOLS,
|
||||
max_pool_size: int = constants.DEFAULT_MAX_POOL_SIZE,
|
||||
) -> None:
|
||||
socket_path = socket_url.replace("http+unix://", "")
|
||||
if not socket_path.startswith("/"):
|
||||
socket_path = "/" + socket_path
|
||||
@@ -86,7 +93,7 @@ class UnixHTTPAdapter(BaseHTTPAdapter):
|
||||
)
|
||||
super().__init__()
|
||||
|
||||
def get_connection(self, url, proxies=None):
|
||||
def get_connection(self, url: str | bytes, proxies=None) -> UnixHTTPConnectionPool:
|
||||
with self.pools.lock:
|
||||
pool = self.pools.get(url)
|
||||
if pool:
|
||||
@@ -99,7 +106,7 @@ class UnixHTTPAdapter(BaseHTTPAdapter):
|
||||
|
||||
return pool
|
||||
|
||||
def request_url(self, request, proxies):
|
||||
def request_url(self, request, proxies) -> str:
|
||||
# The select_proxy utility in requests errors out when the provided URL
|
||||
# does not have a hostname, like is the case when using a UNIX socket.
|
||||
# Since proxies are an irrelevant notion in the case of UNIX sockets
|
||||
|
||||
Reference in New Issue
Block a user